Changes. Contains bug where not all effects are saved into datastructure; checkin...
[IRC.git] / Robust / src / IR / Flat / RuntimeConflictResolver.java
1 package IR.Flat;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.PrintWriter;
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.Hashtable;
9 import java.util.Iterator;
10 import java.util.Set;
11 import Analysis.Disjoint.*;
12 import IR.TypeDescriptor;
13
14 //TODO Make more efficient by only using ONE hashtable. 
15
16 //TODO make threads be aware of each other and add another check for other rblocks or something
17 //to fix issue of sometimes one thread marking conflict and not the other.
18 //TODO it appears that using the optimize flags screws with the invar naming. 
19
20 /* An instance of this class manages all OoOJava coarse-grained runtime conflicts
21  * by generating C-code to either rule out the conflict at runtime or resolve one.
22  * 
23  * How to Use:
24  * 1) Instantiate singleton object
25  * 2a) Call void traverse(FlatSESEEnterNode rblock, Hashtable<Taint, Set<Effect>> effects, Hashtable<Taint, Set<Effect>> conflicts, ReachGraph rg)
26  *    as many times as needed
27  * 2b) call String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) to get the name of the traverse method in C
28  * 3) Call void close()
29  */
30 public class RuntimeConflictResolver {
31   private static boolean debug = false;
32   
33   public static String outputFile;
34   private PrintWriter cFile;
35   private PrintWriter headerFile;
36   private static final String hashAndQueueCFileDir = "oooJava/";
37
38   // initializing variables can be found in printHeader()
39   private static final String getAllocSiteInC = "->allocsite";
40   private static final String queryAndAddHashTableInC = "hashRCRInsert(";
41   private static final String addToQueueInC = "enqueueRCRQueue(";
42   private static final String dequeueFromQueueInC = "dequeueRCRQueue()";
43
44   private static final String clearQueue = "resetRCRQueue()";
45   // Make hashtable; hashRCRCreate(unsigned int size, double loadfactor)
46   private static final String mallocHashTable = "hashRCRCreate(1024, 0.25)";
47   private static final String deallocHashTable = "hashRCRDelete()";
48   private static final String resetHashTable = "hashRCRreset()";
49
50   public RuntimeConflictResolver(String buildir) throws FileNotFoundException {
51     outputFile = buildir + "RuntimeConflictResolver";
52     
53     cFile = new PrintWriter(new File(outputFile + ".c"));
54     headerFile = new PrintWriter(new File(outputFile + ".h"));
55     
56     cFile.append("#include \"" + hashAndQueueCFileDir + "hashRCR.h\"\n#include \""
57         + hashAndQueueCFileDir + "Queue_RCR.h\"\n#include <stdlib.h>\n");
58     cFile.append("#include \"classdefs.h\"\n");
59     cFile.append("#include \"RuntimeConflictResolver.h\"\n");
60     
61     headerFile.append("#ifndef __3_RCR_H_\n");
62     headerFile.append("#define __3_RCR_H_\n");
63     //TODO more closely integrate this by asking generic type from other components? 
64     //generic cast struct
65     cFile.append("struct genericObjectStruct {int type; int oid; int allocsite; int ___cachedCode___; int ___cachedHash___;};\n");
66   }
67
68   /*
69    * Basic steps: 
70    * 1) Create pruned data structures from givens 
71    *     1a) Use effects sets to verify if we can access something (reads) 
72    *     1b) Mark conflicts with 2 flags (one for self, one for references down the line) 
73    * 2) build code output structure 
74    * 3) printout
75    */
76   public void traverse(FlatSESEEnterNode rblock, 
77       Hashtable<Taint, Set<Effect>> effects,
78       Hashtable<Taint, Set<Effect>> conflicts, 
79       ReachGraph rg) {
80     
81     Set<TempDescriptor> inVars = rblock.getInVarSet();
82     
83     if (inVars.size() == 0)
84       return;
85     
86     // For every non-primitive variable, generate unique method
87     // Special Note: The Criteria for executing printCMethod in this loop should match
88     // exactly the criteria in buildcode.java to invoke the generated C method(s). 
89     for (TempDescriptor invar : inVars) {
90       TypeDescriptor type = invar.getType();
91       if(type == null || type.isPrimitive()) {
92         continue;
93       }
94
95       Hashtable<AllocSite, ConcreteRuntimeObjNode> created = new Hashtable<AllocSite, ConcreteRuntimeObjNode>();
96
97       createTree(rblock, invar, effects, conflicts, rg, created);
98       if (!created.isEmpty()) {
99         rblock.addInVarForDynamicCoarseConflictResolution(invar);
100         printCMethod(created, invar.getSafeSymbol(), rblock.getPrettyIdentifier());
101       }
102     }
103   }
104
105   public String getTraverserInvocation(TempDescriptor invar, String varString, FlatSESEEnterNode sese) {
106     return "traverse___" + invar.getSafeSymbol().replaceAll(" ", "") + 
107     sese.getPrettyIdentifier().replaceAll(" ", "") + "___("+varString+");";
108   }
109
110   public void close() {
111     // Adds Extra supporting methods
112     cFile.append("void initializeStructsRCR() { " + mallocHashTable + "; " + clearQueue + "; }");
113     cFile.append("void destroyRCR() { " + deallocHashTable + "; }\n");
114     
115     headerFile.append("void initializeStructsRCR(); \nvoid destroyRCR(); \n");
116     headerFile.append("#endif\n");
117
118     cFile.close();
119     headerFile.close();
120   }
121
122   //TODO it appears that using the optimize flags screws with the invar naming. 
123   private void createTree(FlatSESEEnterNode rblock, 
124       TempDescriptor invar,
125       Hashtable<Taint, Set<Effect>> effects,
126       Hashtable<Taint, Set<Effect>> conflicts, 
127       ReachGraph rg, 
128       Hashtable<AllocSite, ConcreteRuntimeObjNode> created) {
129
130     VariableNode varNode = rg.getVariableNodeNoMutation(invar);
131    
132     //TODO Fix bug where it recognizes multiple effects for the same string ><
133     Hashtable<AllocSite, EffectsGroup> table =
134         generateEffectsLookupTable(rblock, varNode, effects, conflicts);
135     
136     // if table is null that means there's no conflicts, therefore we need not
137     // create a traversal
138     if (table == null)
139       return;
140    
141     if(debug) {
142       System.out.println("==========Table print out============");
143       for(AllocSite key: table.keySet()) {
144         EffectsGroup eg= table.get(key);
145         for(String innerKey: eg.myEffects.keySet()) {
146           EffectPair ef = eg.myEffects.get(innerKey);
147           System.out.println(key.hashCode() + "." + innerKey + "    conflict="+ ef.conflict );
148         }
149       }
150     }
151
152     Iterator<RefEdge> possibleEdges = varNode.iteratorToReferencees();    
153     
154     while (possibleEdges.hasNext()) {
155       RefEdge edge = possibleEdges.next();
156       assert edge != null;
157
158       ConcreteRuntimeObjNode singleRoot = new ConcreteRuntimeObjNode(edge.getDst(), true);
159       AllocSite rootKey = singleRoot.allocSite;
160
161       if (!created.containsKey(rootKey)) {
162         created.put(rootKey, singleRoot);
163         createHelper(singleRoot, edge.getDst().iteratorToReferencees(), created, table);
164       }
165     }
166   }
167
168   private Hashtable<AllocSite, EffectsGroup> generateEffectsLookupTable(FlatSESEEnterNode rblock,
169         VariableNode var, Hashtable<Taint, Set<Effect>> effects,
170         Hashtable<Taint, Set<Effect>> conflicts) {
171       // we search effects since conflicts is only a subset of effects
172       Taint taint = getProperTaint(rblock, var, effects);
173       if (taint == null) {
174         if(debug) {
175           System.out.println("Null FOR " +var.getTempDescriptor().getSafeSymbol() + rblock.toPrettyString());
176         }
177         return null;
178       }
179     
180       Set<Effect> localEffects = effects.get(taint);
181       Set<Effect> localConflicts = conflicts.get(taint);
182       
183       if (localEffects == null || localEffects.isEmpty() || localConflicts == null || localConflicts.isEmpty())
184         return null;
185       
186   //    Debug Code for manually checking effects
187       if(debug) {
188         System.out.println("#### List of Effects/Conflicts ####");
189         System.out.println("For Taint " + taint);
190         System.out.println("Effects");
191         for(Effect e: localEffects)
192         {
193          System.out.println(e); 
194         }
195         
196         System.out.println("Conflicts");
197         for(Effect e: localConflicts)
198         {
199           System.out.println(e); 
200         }
201       }
202       
203       Hashtable<AllocSite, EffectsGroup> lookupTable = new Hashtable<AllocSite, EffectsGroup>();
204       
205       for (Effect e : localEffects) {
206         boolean conflict = localConflicts.contains(e);
207         AllocSite key = e.getAffectedAllocSite();
208         EffectsGroup myEffects = lookupTable.get(key);
209         
210         if(myEffects == null) {
211           myEffects = new EffectsGroup();
212           lookupTable.put(key, myEffects);
213         }
214         
215         if(e.getField().getType().isPrimitive()) {
216           if(conflict) {
217             myEffects.addPrimative(e);
218           }
219         }
220         else {
221           myEffects.addObj(e, conflict);
222         }      
223       }
224       
225       return lookupTable;
226     }
227
228   // Plan is to add stuff to the tree depth-first sort of way. That way, we can
229   // propagate up conflicts
230   private void createHelper(ConcreteRuntimeObjNode curr, 
231                             Iterator<RefEdge> edges, 
232                             Hashtable<AllocSite, ConcreteRuntimeObjNode> created,
233                             Hashtable<AllocSite, EffectsGroup> table) {
234     assert table != null;
235     
236     AllocSite parentKey = curr.allocSite;
237     EffectsGroup currEffects = table.get(parentKey);
238     
239     if (currEffects == null || currEffects.isEmpty()) 
240       return;
241     
242     //Handle Objects
243     if(currEffects.hasObjectConflicts()) {
244       while(edges.hasNext()) {
245         RefEdge edge = edges.next();
246         String field = edge.getField();
247         EffectPair effect = currEffects.getObjEffect(field); // TODO are you certain there is only one effect to get here?
248         //If there is no effect, then there's not point in traversing this edge
249         if(effect != null)
250         {
251           HeapRegionNode childHRN = edge.getDst();
252           AllocSite childKey = childHRN.getAllocSite();
253           boolean isNewChild = !created.containsKey(childKey);
254           ConcreteRuntimeObjNode child; 
255     
256           if(isNewChild) {
257             child = new ConcreteRuntimeObjNode(childHRN, false);
258             created.put(childKey, child);
259           }
260           else {
261             child = created.get(childKey);
262           }
263     
264           curr.addObjChild(field, child, effect.conflict);
265           
266           if (effect.conflict) {
267             propogateObjConflictFlag(child);
268           }
269           
270           if (effect.type == Effect.read && isNewChild) {
271             createHelper(child, childHRN.iteratorToReferencees(), created, table);
272           }
273         }
274       }
275     }
276     
277     //Handle primitives
278     if(currEffects.hasPrimativeConflicts()) {
279       curr.primativeFields = currEffects.primativeConflictingFields; 
280       propogatePrimConflictFlag(curr);
281     } 
282   }
283
284   // This will propagate the conflict up the data structure.
285   private void propogateObjConflictFlag(ConcreteRuntimeObjNode in) {
286     ConcreteRuntimeObjNode node = in;
287     
288     while(node.lastReferencer != null) {
289       node.lastReferencer.decendantsObjConflict = true;
290       if(!node.conflictingParents.add(node.lastReferencer))
291         break;
292       node = node.lastReferencer;
293     }
294   }
295   
296   private void propogatePrimConflictFlag(ConcreteRuntimeObjNode in) {
297     ConcreteRuntimeObjNode node = in;
298     
299     while(node.lastReferencer != null) {
300       node.lastReferencer.decendantsPrimConflict = true;
301       if(!node.conflictingParents.add(node.lastReferencer))
302         break;
303       node = node.lastReferencer;
304     }
305   }
306
307   /*
308    * This method generates a C method for every inset variable and rblock. 
309    * 
310    * The C method works by generating a large switch statement that will run the appropriate 
311    * checking code for each object based on its allocation site. The switch statement is 
312    * surrounded by a while statement which dequeues objects to be checked from a queue. An
313    * object is added to a queue only if it contains a conflict (in itself or in its referencees)
314    *  and we came across it while checking through it's referencer. Because of this property, 
315    *  conflicts will be signaled by the referencer; the only exception is the inset variable which can 
316    *  signal a conflict within itself. 
317    */
318   private void printCMethod(Hashtable<AllocSite, ConcreteRuntimeObjNode> created, String inVar, String rBlock) {
319     //This hash table keeps track of all the case statements generated. Although it may seem a bit much
320     //for its purpose, I think it may come in handy later down the road to do it this way. 
321     //(i.e. what if we want to eliminate some cases? Or build filter for 1 case)
322     Hashtable<AllocSite, StringBuilder> cases = new Hashtable<AllocSite, StringBuilder>();
323     
324     //Generate C cases 
325     for (ConcreteRuntimeObjNode node : created.values()) {
326       // If we haven't seen it and it's a node with more than 1 parent
327       // Note: a node with 0 parents is a root node (i.e. inset variable)
328       if (!cases.containsKey(node.allocSite) && (node.getNumOfReachableParents() != 1 || node.isInsetVar)
329           && node.decendantsConflict()) {
330         //resets the lastReferncer if we're dealing with an insetVar
331         node.lastReferencer = null;
332         addChecker(node, cases, null, "ptr", 0);
333       }
334     }
335     
336     //IMPORTANT: remember to change getTraverserInvocation if you change the line below
337     String methodName = "void traverse___" + inVar.replaceAll(" ", "") + rBlock.replaceAll(" ", "") + 
338     "___(void * InVar)";
339     
340     cFile.append(methodName + " {\n");
341     headerFile.append(methodName + ";\n");
342     
343     cFile.append("printf(\"The traverser ran for " + methodName + "\\n\");\n");
344     
345     if(cases.size() == 0) {
346       cFile.append(" return; }");
347     } 
348     else {
349       //Casts the ptr to a genericObjectStruct so we can get to the ptr->allocsite field. 
350       cFile.append("struct genericObjectStruct * ptr = (struct genericObjectStruct *) InVar;  if(InVar != NULL) { " + queryAndAddHashTableInC
351           + "ptr); do { ");
352       
353       cFile.append("switch(ptr->allocsite) { ");
354       
355       for(AllocSite singleCase: cases.keySet())
356         cFile.append(cases.get(singleCase));
357       
358       cFile.append(" default : break; ");
359       cFile.append("}} while( (ptr = " + dequeueFromQueueInC + ") != NULL); ");
360       
361       //Closes the method by clearing the Queue and reseting the hashTable to prevent
362       //overhead from freeing and mallocing the structures. 
363       cFile.append(clearQueue + "; " + resetHashTable + "; }}\n"); 
364     }
365     cFile.flush();
366   }
367   
368   /*
369    * addChecker creates a case statement for every object that is either an inset variable
370    * or has multiple referencers (incoming edges). Else it just prints the checker for that object
371    * so that its processing can be pushed up to the referencer node. 
372    */
373   private void addChecker(ConcreteRuntimeObjNode node, 
374                           Hashtable<AllocSite,StringBuilder> cases, 
375                           StringBuilder possibleContinuingCase, 
376                           String prefix, 
377                           int depth) {
378     StringBuilder currCase = possibleContinuingCase;
379     // We don't need a case statement for things with either 1 incoming or 0 out
380     // going edges, because they can be processed when checking the parent. 
381     if ((node.getNumOfReachableParents() != 1 || node.isInsetVar)  && node.decendantsConflict()) {
382       assert prefix.equals("ptr") && !cases.containsKey(node.allocSite);
383       currCase = new StringBuilder();
384       cases.put(node.allocSite, currCase);
385       currCase.append("case " + node.getAllocationSite() + ":\n { ");
386     }
387     //either currCase is continuing off a parent case or is its own. 
388     assert currCase !=null;
389     
390     //Specific Primitives test for invars
391     if(node.isInsetVar && node.hasPrimativeConflicts()) {
392       handlePrimitiveConflict(currCase, prefix, node.primativeFields, node.allocSite);
393     }
394     
395     //Casts C pointer; depth is used to create unique "myPtr" name for when things are inlined
396     String currPtr = "myPtr" + depth;
397     String structType = node.original.getType().getSafeSymbol();
398     currCase.append("struct " + structType + " * "+currPtr+"= (struct "+ structType + " * ) " + prefix + "; ");
399   
400     //Handles Objects
401     for (ObjRef ref : node.objectRefs) {
402       // Will only process edge if there is some sort of conflict with the Child
403       if (ref.hasConflictsDownThisPath(node)) {
404         String childPtr = currPtr +"->___" + ref.field + "___";
405         
406         // Checks if the child exists and has allocsite matching the conflict
407         currCase.append("if(" + childPtr + " != NULL && " + childPtr + getAllocSiteInC + "=="
408             + ref.allocSite + ") { ");
409
410         // Prints out conflicts of child
411         if (ref.child.hasConflicts())
412           handleObjConflict(currCase, childPtr, node.allocSite);
413        
414         if(ref.child.hasPrimativeConflicts())
415           handlePrimitiveConflict(currCase, childPtr, ref.child.primativeFields, ref.child.allocSite);
416
417         if (ref.child.decendantsConflict()) {
418           // Checks if we have visited the child before
419           currCase.append("if(!" + queryAndAddHashTableInC + childPtr + ")) { ");
420           if (ref.child.getNumOfReachableParents() == 1 && !ref.child.isInsetVar) {
421             addChecker(ref.child, cases, currCase, childPtr, depth + 1);
422           }
423           else {
424             currCase.append(addToQueueInC + childPtr + ");");
425           }
426           
427           currCase.append(" } ");
428         }
429         currCase.append(" } ");
430       }
431     }
432
433     if ((node.getNumOfReachableParents() != 1 || node.isInsetVar) && node.decendantsConflict())
434       currCase.append(" } break; \n");
435   }
436
437   private void handleObjConflict(StringBuilder currCase, String childPtr, AllocSite allocSite) {
438     currCase.append("printf(\"Conflict detected with %p from parent with allocation site %u\\n\"," + childPtr + "," + allocSite.hashCodeSpecific() + ");");
439   }
440   
441   private void handlePrimitiveConflict(StringBuilder currCase, String ptr, ArrayList<String> conflicts, AllocSite allocSite) {
442     currCase.append("printf(\"Primitive Conflict detected with %p with alloc site %u\\n\", "+ptr+", "+allocSite.hashCodeSpecific()+"); ");
443   }
444
445   private Taint getProperTaint(FlatSESEEnterNode rblock, VariableNode var,
446       Hashtable<Taint, Set<Effect>> effects) {
447     Set<Taint> taints = effects.keySet();
448     
449     for (Taint t : taints) {
450       FlatSESEEnterNode sese = t.getSESE();
451       //Jim says that this case should never happen, but it may
452       if( sese == null ) {
453           System.out.println( "What should I do with a stall site taint? --Jim");
454       }
455       if(sese != null && sese.equals(rblock) && t.getVar().equals(var.getTempDescriptor())) {
456         return t;
457       }
458     }
459     return null;
460   }
461
462   private class EffectsGroup
463   {
464     Hashtable<String, EffectPair> myEffects;
465     ArrayList<String> primativeConflictingFields;
466     
467     public EffectsGroup() {
468       myEffects = new Hashtable<String, EffectPair>();
469       primativeConflictingFields = new ArrayList<String>();
470     }
471
472     public void addPrimative(Effect e) {
473       primativeConflictingFields.add(e.getField().toPrettyStringBrief());
474     }
475     
476     public void addObj(Effect e, boolean conflict) {
477       EffectPair effect = new EffectPair(e, conflict);
478       myEffects.put(e.getField().getSymbol(), effect);
479     }
480     
481     public boolean isEmpty() {
482       return myEffects.isEmpty() && primativeConflictingFields.isEmpty();
483     }
484     
485     public boolean hasPrimativeConflicts(){
486       return !primativeConflictingFields.isEmpty();
487     }
488     
489     public boolean hasObjectConflicts() {
490       return !myEffects.isEmpty();
491     }
492     
493     public EffectPair getObjEffect(String field) {
494       return myEffects.get(field);
495     }
496   }
497   
498   private class EffectPair {
499     Effect originalEffect;
500     int type;
501     boolean conflict;
502
503     public EffectPair(Effect e, boolean conflict) {
504       originalEffect = e;
505       type = e.getType();
506       this.conflict = conflict;
507     }
508
509     public int hashCode() {
510       return originalEffect.hashCode();
511     }
512
513     public boolean equals(Object o) {
514       if (o == null)
515         return false;
516
517       if (!(o instanceof EffectPair))
518         return false;
519
520       EffectPair other = (EffectPair) o;
521
522       return (other.originalEffect.getAffectedAllocSite().equals(
523           originalEffect.getAffectedAllocSite()) && other.originalEffect.getField().equals(
524           originalEffect.getField()));
525     }
526     
527     public String toString()
528     {
529       return originalEffect.toString();
530     }
531   }
532
533   private class ObjRef {
534     String field;
535     int allocSite;
536     //This keeps track of the parent that we need to pass by inorder to get
537     //to the conflicting child (if there is one). 
538     ConcreteRuntimeObjNode parentPathToMe;
539     ConcreteRuntimeObjNode child;
540
541     public ObjRef(String fieldname, 
542                   ConcreteRuntimeObjNode ref, 
543                   boolean con, 
544                   ConcreteRuntimeObjNode grandParent) {
545       field = fieldname;
546       allocSite = ref.getAllocationSite();
547       child = ref;
548       parentPathToMe = con ? grandParent : null;
549     }
550     
551     public boolean hasConflictsDownThisPath(ConcreteRuntimeObjNode curr) {
552       if(!child.hasConflicts())
553         return false;
554       
555       if(curr.conflictingParents.isEmpty() && curr.isInsetVar)
556         return true;
557       
558       for(ConcreteRuntimeObjNode parent: curr.conflictingParents)
559         // we can do a == since it will LITTERALLY be the same object. 
560         if(parent == parentPathToMe)
561           return true;
562       
563       return false;
564     }
565   }
566
567   private class ConcreteRuntimeObjNode {
568     ArrayList<ObjRef> objectRefs;
569     ArrayList<String> primativeFields;
570     ArrayList<ConcreteRuntimeObjNode> parents;
571     HashSet<ConcreteRuntimeObjNode> conflictingParents;
572     ConcreteRuntimeObjNode lastReferencer;
573     boolean decendantsPrimConflict;
574     boolean decendantsObjConflict;
575     boolean isInsetVar;
576     AllocSite allocSite;
577     HeapRegionNode original;
578
579     public ConcreteRuntimeObjNode(HeapRegionNode me, boolean isInVar) {
580       objectRefs = new ArrayList<ObjRef>();
581       parents = new ArrayList<ConcreteRuntimeObjNode>();
582       conflictingParents = new HashSet<ConcreteRuntimeObjNode>();
583       lastReferencer = null;
584       allocSite = me.getAllocSite();
585       original = me;
586       isInsetVar = isInVar;
587       decendantsPrimConflict = false;
588       decendantsObjConflict = false;
589       primativeFields = null;
590     }
591
592     @Override
593     public int hashCode() {
594       // This gets allocsite number
595       return allocSite.hashCodeSpecific();
596     }
597
598     @Override
599     public boolean equals(Object obj) {
600       return original.equals(obj);
601     }
602
603     public int getAllocationSite() {
604       return allocSite.hashCodeSpecific();
605     }
606
607     public int getNumOfReachableParents() {
608       return conflictingParents.size();
609     }
610     
611     public boolean hasPrimativeConflicts() {
612       return primativeFields != null;
613     }
614     
615     public boolean hasConflicts() {
616       return (primativeFields != null) || !conflictingParents.isEmpty();
617     }
618     
619     public boolean decendantsConflict() {
620       return decendantsPrimConflict || decendantsObjConflict;
621     }
622
623     public void addObjChild(String field, ConcreteRuntimeObjNode child, boolean conflict) {
624       child.lastReferencer = this;
625       ObjRef ref = new ObjRef(field, child, conflict, this.lastReferencer);
626       objectRefs.add(ref);
627       child.parents.add(this);
628     }
629     
630     public String toString()
631     {
632       return "AllocSite=" + getAllocationSite() + " myConflict=" + !conflictingParents.isEmpty() + 
633               " decCon="+decendantsObjConflict+ " NumOfParents=" + parents.size()+ 
634               " NumOfConParents=" + getNumOfReachableParents() + " ObjectChildren=" + objectRefs.size();
635     }
636   }
637 }