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