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}