001package daikon.chicory; 002 003import daikon.Chicory; 004import daikon.FileIO; 005import daikon.PptTopLevel.PptType; 006import daikon.plumelib.bcelutil.SimpleLog; 007import java.io.PrintWriter; 008import java.lang.reflect.Member; 009import java.time.LocalDateTime; 010import java.time.ZoneId; 011import java.util.ArrayList; 012import java.util.EnumSet; 013import java.util.HashSet; 014import java.util.List; 015import java.util.Locale; 016import java.util.Set; 017import java.util.regex.Pattern; 018import org.checkerframework.checker.lock.qual.GuardSatisfied; 019import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 020import org.checkerframework.checker.nullness.qual.Nullable; 021import org.checkerframework.dataflow.qual.Pure; 022import org.checkerframework.dataflow.qual.SideEffectFree; 023 024/** 025 * DeclWriter writes the {@code .decls} file to a stream. It uses traversal pattern trees (see 026 * {@link DaikonVariableInfo}) for each program point. These are also used by the {@link 027 * DTraceWriter}. 028 */ 029@SuppressWarnings("nullness") // to do 030public class DeclWriter extends DaikonWriter implements ComparabilityProvider { 031 // Notes: 032 // 033 // Class.getName() returns JVM names (eg, [Ljava.lang.String;) 034 035 /** Debug flag set from Chicory.debug_decl_print. */ 036 public boolean debug = false; 037 038 // If the --comparability-file option is active, there might be 039 // variables for which DynComp saw no interactions and did not 040 // generate a comparability value. In order to reduce the number 041 // of useless invariants (when running Daikon on the output of 042 // Chicory), we generate a unique, dummy comparability value in 043 // these cases. We start at the maximum integer and decrement 044 // for each use. At the start of each method, object or class 045 // we reset the value back - though it's hard to imagine a case 046 // where there would be a collision with DynComp's values which 047 // increment up from an initial value of 1. 048 private static int initial_compare_value = Integer.MAX_VALUE; 049 private static int unique_compare_value; 050 051 /** Stream to write to. */ 052 private PrintWriter outFile; 053 054 /** 055 * Enable parent relations other than methods to their class objects. Turned off for now to match 056 * previous behavior. 057 */ 058 private static boolean enable_object_user = false; 059 060 /** 061 * Constructs a DeclWriter, preparing it to receive messages. 062 * 063 * @param writer stream to write to 064 */ 065 public DeclWriter(PrintWriter writer) { 066 super(); 067 outFile = writer; 068 debug = Chicory.debug_decl_print; 069 } 070 071 /** 072 * Prints header information to the decls file. Should be called once before emitting any other 073 * declarations. 074 * 075 * @param className name of the top-level class (used only for printing comments) 076 */ 077 public void printHeaderInfo(String className) { 078 outFile.println("// Declarations for " + className); 079 outFile.println( 080 "// Declarations written by Chicory " + LocalDateTime.now(ZoneId.systemDefault())); 081 outFile.println(); 082 083 // Determine comparability string 084 String comparability = "none"; 085 if (Runtime.comp_info != null) { 086 comparability = "implicit"; 087 } 088 outFile.printf("decl-version 2.0%n"); 089 outFile.printf("var-comparability %s%n%n", comparability); 090 } 091 092 /** 093 * Returns the correctly formulated ":::OBJECT" name of the class (i.e., the program point name) 094 * 095 * @param type the ClassType type 096 * @return the correctly formulated String 097 */ 098 public static String classObjectName(Class<?> type) { 099 return (type.getName() + ":::OBJECT"); 100 } 101 102 /** 103 * Prints declarations for all the methods in the indicated class. This method is called at run 104 * time to print decls info for a class. 105 * 106 * @param cinfo class whose declarations should be printed 107 * @param comp_info comparability information 108 */ 109 public void printDeclClass(ClassInfo cinfo, @Nullable DeclReader comp_info) { 110 111 if (debug) { 112 System.out.println("Enter printDeclClass: " + cinfo); 113 } 114 115 // Print all methods and constructors 116 for (MethodInfo mi : cinfo.get_method_infos()) { 117 118 Member member = mi.member; 119 120 // Don't want to instrument these types of methods 121 if (!shouldInstrumentMethod(member)) { 122 continue; 123 } 124 125 // Gset the root of the method's traversal pattern 126 RootInfo enterRoot = mi.traversalEnter; 127 assert enterRoot != null : "Traversal pattern not initialized at method " + mi.method_name; 128 129 String entryName = 130 (member != null 131 ? methodEntryName(member) 132 : mi.class_info.class_name + ".<clinit>" + FileIO.enter_tag); 133 print_method(mi, enterRoot, entryName, PptType.ENTER, comp_info); 134 135 // Print exit program point for EACH exit location in the method 136 // Note that there may not be any exits. They may get filtered out, 137 // or some methods don't have an exit (only a throw) 138 Set<Integer> theExits = new HashSet<>(mi.exit_locations); 139 for (Integer exitLoc : theExits) { 140 // Get the root of the method's traversal pattern 141 RootInfo exitRoot = mi.traversalExit; 142 assert enterRoot != null : "Traversal pattern not initialized at method " + mi.method_name; 143 144 String exitName = 145 (member != null 146 ? methodExitName(member, exitLoc) 147 : mi.class_info.class_name + ".<clinit>" + FileIO.exit_tag + exitLoc); 148 print_method(mi, exitRoot, exitName, PptType.SUBEXIT, comp_info); 149 } 150 } 151 152 print_class_ppt(cinfo, cinfo.class_name + ":::CLASS", comp_info); 153 print_object_ppt(cinfo, classObjectName(cinfo.clazz), comp_info); 154 155 if (debug) { 156 System.out.println("Exit printDeclClass"); 157 } 158 } 159 160 /** 161 * Prints a method's program point. This includes the ppt declaration, all of the ppt records, and 162 * records for each variable. 163 * 164 * <p>This method uses variable information from the traversal tree. 165 * 166 * @param mi the method information for the method 167 * @param root the root of the traversal tree 168 * @param name the program point name 169 * @param ppt_type the type of the program point (enter, exit, etc) 170 * @param comp_info comparability information 171 */ 172 private void print_method( 173 MethodInfo mi, RootInfo root, String name, PptType ppt_type, @Nullable DeclReader comp_info) { 174 175 if (debug) { 176 System.out.println("Enter print_method: " + name); 177 } 178 179 // reset dummy comparability value 180 unique_compare_value = initial_compare_value; 181 182 outFile.println("ppt " + escape(name)); 183 184 outFile.println("ppt-type " + ppt_type.name().toLowerCase(Locale.ENGLISH)); 185 186 // Look for and print any hierarchy relations 187 List<VarRelation> relations = new ArrayList<>(); 188 for (DaikonVariableInfo child : root) { 189 find_relations(null, mi.is_static(), null, child, relations); 190 } 191 for (VarRelation relation : relations) { 192 outFile.println("parent parent " + relation.parent_ppt_name + " " + relation.id); 193 } 194 195 // Print each variable 196 for (DaikonVariableInfo childOfRoot : root) { 197 if (debug) { 198 System.out.println("method var: " + childOfRoot.getName()); 199 } 200 traverse_decl( 201 null, 202 mi.is_static(), 203 null, 204 childOfRoot, 205 null, 206 relations, 207 ((comp_info == null) ? null : comp_info.find_ppt(name))); 208 } 209 210 outFile.println(); 211 212 if (debug) { 213 System.out.println("Exit print_method "); 214 } 215 } 216 217 /** 218 * Prints the class program point. This contains only the static variables. If there are no static 219 * variables to print, this method does nothing. 220 * 221 * @param cinfo class whose static declarations should be printed 222 * @param name the program point name 223 * @param comp_info comparability information 224 */ 225 private void print_class_ppt(ClassInfo cinfo, String name, DeclReader comp_info) { 226 227 if (debug) { 228 System.out.println("Enter print_class_ppt: " + cinfo); 229 } 230 231 // reset dummy comparability value 232 unique_compare_value = initial_compare_value; 233 234 if (num_class_vars(cinfo) > 0) { 235 236 outFile.println("ppt " + escape(name)); 237 outFile.println("ppt-type class"); 238 239 // Print out the static fields 240 for (DaikonVariableInfo childOfRoot : RootInfo.getClassPpt(cinfo, Runtime.nesting_depth)) { 241 if (debug) { 242 System.out.println("class var: " + childOfRoot.getName()); 243 } 244 traverse_decl( 245 null, 246 false, 247 null, 248 childOfRoot, 249 null, 250 null, 251 ((comp_info == null) ? null : comp_info.find_ppt(name))); 252 } 253 254 outFile.println(); 255 } 256 257 if (debug) { 258 System.out.println("Exit print_class_ppt"); 259 } 260 } 261 262 /** 263 * Prints the object program point. This contains the "this" object and the class' fields. 264 * 265 * @param cinfo class whose object program point should be printed 266 * @param name the program point name 267 * @param comp_info comparability information 268 */ 269 private void print_object_ppt(ClassInfo cinfo, String name, DeclReader comp_info) { 270 271 if (debug) { 272 System.out.println("Enter print_object_ppt: " + cinfo); 273 } 274 275 // reset dummy comparability value 276 unique_compare_value = initial_compare_value; 277 278 outFile.println("ppt " + escape(name)); 279 outFile.println("ppt-type object"); 280 RootInfo root = RootInfo.getObjectPpt(cinfo, Runtime.nesting_depth); 281 282 // If there are any static variables, add the relation to 283 // the class ppt 284 List<VarRelation> relations = new ArrayList<>(); 285 if (num_class_vars(cinfo) > 0) { 286 VarRelation relation = new VarRelation(cinfo.class_name + ":::CLASS", "parent"); 287 relation.id = 1; 288 relations.add(relation); 289 } 290 291 // Look for and print any object-user relations 292 for (DaikonVariableInfo child : root) { 293 find_relations(cinfo, false, null, child, relations); 294 } 295 for (VarRelation relation : relations) { 296 outFile.println( 297 "parent " + relation.type + " " + relation.parent_ppt_name + " " + relation.id); 298 } 299 300 // Write out the variables 301 for (DaikonVariableInfo childOfRoot : root) { 302 if (debug) { 303 System.out.println("object var: " + childOfRoot.getName()); 304 } 305 traverse_decl( 306 cinfo, 307 false, 308 null, 309 childOfRoot, 310 null, 311 relations, 312 ((comp_info == null) ? null : comp_info.find_ppt(name))); 313 } 314 315 outFile.println(); 316 317 if (debug) { 318 System.out.println("Exit print_object_ppt"); 319 } 320 } 321 322 /** 323 * Object program points are constructed for invariants about an object. We define an object 324 * invariant as one that is true at the entrance and exit of each public method and also each time 325 * an instance of the object is available to another method (eg, when it is passed as a parameter 326 * or available as a static). Daikon implements object invariants by merging the invariants from 327 * each public method and each user of the object. We refer to the relationship between variables 328 * at these program points as a Program point / variable hierarchy. This relationship must be 329 * defined in the declaration record. The VarRelation class tracks one relation. 330 */ 331 private static class VarRelation { 332 /** Name of the program point for the parent. */ 333 String parent_ppt_name; 334 335 /** Prefix of the variable name that is not part of the parent name. */ 336 String local_prefix; 337 338 /** Prefix of the parent that replaces the local prefix. Normally 'this'. */ 339 String parent_prefix; 340 341 /** Top level variable for the relation. */ 342 String local_variable; 343 344 /** Type of the relation (parent, user, etc) */ 345 String type; 346 347 /** Number that identifies this relation within this ppt. */ 348 int id; 349 350 static SimpleLog debug = new SimpleLog(false); 351 352 /** Create a VarRelation. */ 353 public VarRelation( 354 String parent_ppt_name, 355 String type, 356 String local_prefix, 357 String parent_prefix, 358 String local_variable) { 359 this.parent_ppt_name = parent_ppt_name; 360 this.type = type; 361 this.local_prefix = local_prefix; 362 this.parent_prefix = parent_prefix; 363 this.local_variable = local_variable; 364 debug.log("Created %s", this); 365 } 366 367 /** Create a var relation with the matching names. */ 368 public VarRelation(String parent_ppt_name, String type) { 369 this(parent_ppt_name, type, null, null, null); 370 } 371 372 @SideEffectFree 373 @Override 374 public String toString(@GuardSatisfied VarRelation this) { 375 return String.format( 376 "VarRelation %s (%s->%s) %s [%s]", 377 parent_ppt_name, local_prefix, parent_prefix, local_variable, type); 378 } 379 380 /** 381 * Returns whether or not this relation is from a static variable in an object ppt to its 382 * matching variable at the class level. 383 */ 384 @Pure 385 public boolean is_class_relation() { 386 return parent_ppt_name.endsWith(":::CLASS"); 387 } 388 389 /** 390 * Returns the string defining the relation for the specified variable The format is 391 * parent-ppt-name id parent-variable-name. If the variable is static, it always has the same 392 * name in the parent (since fully specified names are used for static variables). 393 */ 394 public String relation_str(DaikonVariableInfo var) { 395 String out = parent_ppt_name + " " + id; 396 if (!var.isStatic() && (local_prefix != null) && !local_prefix.equals(parent_prefix)) { 397 out += " " + var.getName().replaceFirst(Pattern.quote(local_prefix), parent_prefix); 398 } 399 return out; 400 } 401 402 /** Two VarRelations are equal if the refer to the same program point and local variable. */ 403 @Override 404 @EnsuresNonNullIf(result = true, expression = "#1") 405 @Pure 406 public boolean equals(@GuardSatisfied VarRelation this, @GuardSatisfied @Nullable Object o) { 407 if (!(o instanceof VarRelation) || (o == null)) { 408 return false; 409 } 410 VarRelation vr = (VarRelation) o; 411 return (vr.parent_ppt_name.equals(parent_ppt_name) 412 && (((vr.local_variable == null) && local_variable == null) 413 || ((vr.local_variable != null) && vr.local_variable.equals(local_variable)))); 414 } 415 416 @Override 417 @Pure 418 public int hashCode(@GuardSatisfied VarRelation this) { 419 return (parent_ppt_name.hashCode() 420 + ((local_variable == null) ? 0 : local_variable.hashCode())); 421 } 422 } 423 424 /** 425 * Prints the .decls information for a single DaikonVariableInfo object, and recurses on its 426 * children. If the current variable has comparability defined in compare_ppt, that comparability 427 * is used. Otherwise -1 is used if there is comparability information available and the 428 * information in the variable is used if it is not. 429 */ 430 private void traverse_decl( 431 ClassInfo cinfo, 432 boolean is_static_method, 433 DaikonVariableInfo parent, 434 DaikonVariableInfo var, 435 VarRelation relation, 436 List<VarRelation> relations, 437 DeclReader.DeclPpt compare_ppt) { 438 439 if (debug) { 440 System.out.println("Enter traverse_decl: " + cinfo + ", " + var + ", " + parent); 441 } 442 443 if (!var.declShouldPrint()) { 444 // don't do anything 445 } else if (!(var instanceof StaticObjInfo)) { 446 447 printDecl(parent, var, compare_ppt, Runtime.decl_writer); 448 449 // Determine if there is a ppt for variables of this type 450 // If found this should match one of the previously found relations 451 // for this ppt. 452 // Once a relation has been found, we don't look for recursive 453 // relationships 454 if ((relation == null) && (relations != null)) { 455 relation = find_relation(cinfo, is_static_method, parent, var); 456 if (relation != null) { 457 // System.out.printf("Found relation %s, variable %s%n", relation, 458 // var); 459 int index = relations.indexOf(relation); 460 assert (index != -1) : "Relation " + relation + " not found in " + relations; 461 relation = relations.get(index); 462 } 463 } 464 465 // Put out the variable relation (if one exists). 466 if (relation != null) { 467 outFile.println(" parent " + relation.relation_str(var)); 468 } 469 470 } else { // this is the dummy root for class statics 471 if ((relations != null) && (relations.size() > 0)) { 472 relation = find_relation(cinfo, true, parent, var); 473 if (relation != null) { 474 int index = relations.indexOf(relation); 475 assert (index != -1) : "Relation " + relation + " not found in " + relations; 476 relation = relations.get(index); 477 // System.out.printf("Found class relation %s for cinfo %s%n", 478 // relation, cinfo); 479 } else { 480 System.out.printf("No class relation found for cinfo %s%n", cinfo); 481 } 482 } 483 } 484 485 // Go through all of the current node's children 486 // and recurse 487 for (DaikonVariableInfo child : var) { 488 if (debug) { 489 System.out.println("traverse var: " + child.getName()); 490 } 491 traverse_decl(cinfo, is_static_method, var, child, relation, relations, compare_ppt); 492 } 493 494 if (debug) { 495 System.out.println("Exit traverse_decl"); 496 } 497 } 498 499 /** 500 * Returns the string to write to the output file for the specified enum. Currently this is just 501 * the name of the enum in lower case. 502 * 503 * @param e enum to get name of 504 * @return the name of the enum 505 */ 506 private String out_name(Enum<?> e) { 507 return e.name().toLowerCase(Locale.ENGLISH); 508 } 509 510 /** 511 * Output most of the decl file information for a single variable. This includes the variable, 512 * var-kind, enclosing-var, array, dec-type, rep-type, constant, function-args, flags, and 513 * comparability records. Most notably, it does not output the parent record. The records are 514 * output via the PrinterWriter passed as an argument to the DeclWriter constructor. 515 * 516 * @param parent parent of var in the variable tree 517 * @param var variable whose values are to be output 518 * @param compare_ppt ppt with compare value if comparability-file present, null otherwise 519 * @param comparabilityProvider object on which {@link ComparabilityProvider#getComparability} is 520 * called. It might be this DeclWriter itself. 521 */ 522 public void printDecl( 523 DaikonVariableInfo parent, 524 DaikonVariableInfo var, 525 DeclReader.DeclPpt compare_ppt, 526 ComparabilityProvider comparabilityProvider) { 527 528 // Write out the variable and its name 529 outFile.println("variable " + escape(var.getName())); 530 531 // Write out the kind of variable and its relative name 532 VarKind kind = var.get_var_kind(); 533 String relative_name = var.get_relative_name(); 534 outFile.print(" var-kind " + out_name(kind)); 535 if (relative_name != null) { 536 outFile.print(" " + relative_name); 537 } 538 outFile.println(); 539 540 // Write out the enclosing variable. 541 // If we are in an inner class, we need to special case the 542 // 'hidden' field that holds the outer class 'this' pointer. 543 // If the field name ends with ".this", it can only be this 544 // special case and we need to not output the enclosing-var. 545 if ((parent != null) 546 && !var.isStatic() 547 && !((relative_name != null) && relative_name.endsWith(".this"))) { 548 if (debug) { 549 System.out.println("traverse var parent: " + parent.getName()); 550 } 551 outFile.println(" enclosing-var " + escape(parent.getName())); 552 } 553 554 // If this variable has multiple value, indicate it is an array 555 if (var.isArray()) { 556 outFile.println(" array 1"); 557 } 558 559 // Write out the declared and representation types 560 outFile.println(" dec-type " + escape(var.getTypeNameOnly())); 561 outFile.println(" rep-type " + escape(var.getRepTypeNameOnly())); 562 563 // Write out the constant value (if present) 564 String const_val = var.get_const_val(); 565 if (const_val != null) { 566 outFile.println(" constant " + const_val); 567 } 568 569 // Write out the arguments used to create (if present) the variable if it is a function 570 String function_args = var.get_function_args(); 571 if (function_args != null) { 572 outFile.println(" function-args " + function_args); 573 } 574 575 // Write out the variable flags if any are set 576 EnumSet<VarFlags> var_flags = var.get_var_flags(); 577 if (var_flags.size() > 0) { 578 outFile.print(" flags"); 579 for (Enum<?> e : var_flags) { 580 outFile.print(" " + out_name(e)); 581 } 582 outFile.println(); 583 } 584 585 // Determine comparability and write it out 586 String comp_str = comparabilityProvider.getComparability(var, compare_ppt); 587 outFile.println(" comparability " + comp_str); 588 } 589 590 /** 591 * Get the caparability value for a varaible. 592 * 593 * @param var variable whose value is desired 594 * @param compare_ppt ppt with compare value if comparability-file present, null otherwise 595 * @return String containing the comparability value 596 */ 597 @Override 598 public String getComparability(DaikonVariableInfo var, DeclReader.DeclPpt compare_ppt) { 599 // Currently, the value returned by getCompareString() is always 22. 600 String comp_str = var.getCompareString(); 601 if (compare_ppt != null) { 602 comp_str = "-1"; 603 DeclReader.DeclVarInfo varinfo = compare_ppt.find_var(var.getName()); 604 if (varinfo != null) { 605 comp_str = varinfo.get_comparability(); 606 } 607 } else { 608 // Check to see if DynComp data is present. 609 if (Runtime.comp_info != null) { 610 // There is no comparability value for this variable as DynComp 611 // saw no interactions. In order to reduce the number of useless 612 // invariants, we will generate a unique, dummy comparability value. 613 comp_str = Integer.toString(unique_compare_value--); 614 if (var.isArray()) { 615 // Should output n index values to match number of dimensions. 616 // However, that value is hard to obtain at this point, so just 617 // always do one. May cause some multi-dimension variables to 618 // be put into incorrect comparability set. This is certainly 619 // no worse than previous algorithm. (markro) 620 comp_str = comp_str + "[" + Integer.toString(unique_compare_value--) + "]"; 621 } 622 } 623 } 624 return comp_str; 625 } 626 627 /** 628 * Looks to see if there is a class that we are instrumenting that matches the type of this 629 * variable. If so, returns a VarRelation that describes the hierarchy relationship between this 630 * variable (and its field) and the variables within the object ppt for the class. If this is an 631 * object ppt (ci != null), then each top level static variable has a relation to the class ppt. 632 * 633 * @param cinfo class of the object ppt. Null if this is not an object ppt. 634 * @param is_static_method true if this ppt is a static method enter 635 * @param parent parent of var in the variable tree 636 * @param var variable whose relation is desired 637 */ 638 private @Nullable VarRelation find_relation( 639 @Nullable ClassInfo cinfo, 640 boolean is_static_method, 641 DaikonVariableInfo parent, 642 DaikonVariableInfo var) { 643 644 // Look for object->class static relationship. This starts on each 645 // static variable under 'this' (the static variables of a class are 646 // placed in the CLASS ppt). 647 if (cinfo != null && var.isStatic() && (parent instanceof ThisObjInfo)) { 648 return new VarRelation(cinfo.class_name + ":::CLASS", "parent"); 649 } 650 651 // Only hashcodes have object ppts 652 if (!var.getRepTypeNameOnly().equals("hashcode")) { 653 return null; 654 } 655 656 // Get the type (class) of this variable 657 String decl_type = var.getTypeNameOnly(); 658 // System.out.printf("Looking for hierarchy type %s%n", decl_type); 659 660 // If this ppt is the object ppt for this type, don't create a relation 661 // to it. 662 if ((cinfo != null) && cinfo.class_name.equals(decl_type)) { 663 return null; 664 } 665 666 // Look to see if we are instrumenting this class. If we are, then 667 // there should be an object ppt for this class. If this is a static 668 // method and the relation is over the dummy static variable, it 669 // relates directly to the class ppt, otherwise to the object 670 // ppt. Note that a relation to the class ppt is returned only if there 671 // are static variables. 672 for (ClassInfo ci : SharedData.all_classes) { 673 if (ci.class_name.equals(decl_type)) { 674 // System.out.printf("*Found match for %s : %s%n", decl_type, ci); 675 String ppt_marker = ":::OBJECT"; 676 if (is_static_method && (var instanceof StaticObjInfo)) { 677 // System.out.printf("num_class_vars for classinfo %s%n", ci); 678 if (num_class_vars(ci) == 0) { 679 return null; 680 } 681 ppt_marker = ":::CLASS"; 682 } 683 if (!enable_object_user && !var.getName().equals("this")) { 684 return null; 685 } 686 return new VarRelation( 687 decl_type + ppt_marker, "parent", var.getName(), "this", var.getName()); 688 } 689 } 690 691 return null; 692 } 693 694 /** 695 * Looks for all of the object-user ppt/variable hiearchy relations beginning at var. Once a 696 * relation is found, no more relations are looked for under that variable. In most cases, it 697 * would be expected that only one relation will be found (either var is a class with a 698 * corresponding object ppt or it is not). However, depending on what classes are being 699 * instrumented, it might be possible for a class not to have an object ppt while multiple 700 * children do have object ppts. 701 * 702 * <p>Any relations that are found are added to the relations list. 703 */ 704 private void find_relations( 705 ClassInfo ci, 706 boolean is_static_method, 707 DaikonVariableInfo parent, 708 DaikonVariableInfo var, 709 List<VarRelation> relations) { 710 711 // If there is a new relation for this variable add it to the list and 712 // return it. Note that each static variable in an object ppt will 713 // have a relation to the matching static variable in class ppt. Only 714 // one of these should go in the list of relations. 715 VarRelation relation = find_relation(ci, is_static_method, parent, var); 716 if (relation != null) { 717 if ((relations.size() == 0) 718 || (relations.get(0).is_class_relation() && relation.is_class_relation())) { 719 relations.add(relation); 720 relation.id = relations.size(); 721 return; 722 } 723 } 724 725 // Look for a relation in each child. 726 for (DaikonVariableInfo child : var) { 727 find_relations(ci, is_static_method, parent, child, relations); 728 } 729 } 730 731 /** 732 * Returns the number of variables in the CLASS program point. The CLASS ppt contains all of the 733 * static variables in the class (if any). 734 */ 735 private int num_class_vars(ClassInfo cinfo) { 736 737 RootInfo class_root = RootInfo.getClassPpt(cinfo, Runtime.nesting_depth); 738 assert class_root.children.size() == 1; 739 DaikonVariableInfo static_root = class_root.children.get(0); 740 return static_root.children.size(); 741 } 742}