// expand.beh -- Static traversal expansion // $Id: expand.beh,v 1.10 2002/07/26 05:28:34 dougo Exp $ Program { /** Expand all static traversals into traversal methods attached to each class. Return true iff all traversals are non-empty. */ private boolean expandStaticTraversals() bypassing VisitorSpec via TraversalDef to-stop { TraversalName, TraversalParms, // StrategyExpression } StrategyGraph, PathDirective, StrategyVariable, CompoundStrategy } { (@ boolean path_errors = false; @) (@ ClassGraph cg; @) before ClassGraph (@ cg = host; @) (@ ClassDef source; ClassName clname; @) before ClassDef (@ source = host; clname = host.get_classname(); @) (@ TraversalName name; @) before TraversalName (@ name = host; @) (@ TraversalParms parms; @) before TraversalParms (@ parms = host; @) (@ StrategyGraph sg; @) before StrategyExpression (@ sg = host.toGraph(clname); @) after TraversalDef (@ Program.log.print(" Computing traversal graph for " + name + "..."); Program.log.flush(); TraversalGraph tg = sg.computeTraversalGraph(cg); if (tg == null) { System.err.println("Error: No path found."); path_errors = true; return; } Program.prog.collectStatistics(clname, name, cg, sg, tg); Program.log.println(" generating traversal methods..."); TraversalMethodName mname = new TraversalMethodName(clname, name); String args = parms.collectVisitorNames(); boolean use_nodeset = sg.size() > 1; String start_call = " public void " + name + parms + " {\n"; if (use_nodeset) { start_call +=" java.util.BitSet startSet = new java.util.BitSet();\n"; start_call += sg.startSetCode(" ", "startSet", clname); } start_call += " " + mname + "("; if (use_nodeset) { start_call += "startSet"; if (!args.equals("")) start_call += ", "; } start_call += args + ");\n }\n"; source.addMethod(start_call); Program.prog.makeTraversalMethods(tg, mname, parms, args, use_nodeset); @) finish {{ Program.prog.saveStatistics(); }} return (@ !path_errors @) } } TraversalParms { String collectVisitorNames() = allVisitorNames { (@ boolean first = true; @) init (@ return_val = ""; @) before VisitorName (@ if (first) first = false; else return_val += ", "; return_val += host; @) } } Program { void makeTraversalMethods(TraversalGraph tg, TraversalMethodName name, TraversalParms parms, String args, boolean use_nodeset) (@ TraversalParms revparms = parms.reverse(); String args_with_nodeset = (use_nodeset ? "nodes, " : "") + args; Enumeration vertices = tg.vertices(); while (vertices.hasMoreElements()) { ClassDef def = findClassDef((ClassName) vertices.nextElement()); if (def == null) { // Terminal or external class; ignore. } else if (def.isInterface()) { // Ignore. } else def.makeTraversalMethods(tg, name, parms, revparms, args, args_with_nodeset, use_nodeset); } @) } ClassDef { void makeTraversalMethods(TraversalGraph tg, TraversalMethodName name, TraversalParms parms, TraversalParms revparms, String args, String args_with_nodeset, boolean use_nodeset) = allEdges { (@ ClassDef def; ClassName source; boolean opt; String edge_calls; @) around ClassDef (@ def = host; ClassDef parent = host.get_superclass_def(); if (parent != null && !tg.hasVertex(parent.get_classname())) // The parent is not in the traversal graph, so ignore it. parent = null; /* This is what the code ought to be doing (but it isn't currently): let v be the first visitor outer: If v has an around method for this class, call/cc else call befores on v for this class & its parents if v is last visitor, for each edge e, inner: call befores on v for e if v is last visitor, traverse e else call inner: with next visitor call afters on v for e else call outer: with next visitor call afters on v for this class & its parents */ source = host.get_classname(); Glob glob = new ClassGlob(source); String indent = " "; // Make the before method caller. String bef_super_call = ""; if (parent != null) { bef_super_call = indent + "super." + name.beforeName() + "(" + args + ");\n"; } host.addBeforeMethod(name, parms, bef_super_call + parms.callBefores(glob, indent)); // Make the after method caller. String aft_super_call = ""; if (parent != null) { aft_super_call = indent + "super." + name.afterName() + "(" + args + ");\n"; } host.addAfterMethod(name, parms, revparms.callAfters(glob, indent) + aft_super_call); edge_calls = ""; subtraversal.apply(); String body = host.maybeAddAroundMethod(name, parms, glob, edge_calls, parms.callArounds(glob, indent, source, name, args_with_nodeset), use_nodeset); if (host.isConstructionClass()) { body = indent + name.beforeName() + "(" + args +");\n" + body + indent + name.afterName() + "(" + args +");\n"; } if (use_nodeset) body = tg.intercopyEdgesCode(source, indent, "nodes") + body; host.addTraversalMethod(name, parms, body, use_nodeset); @) before OptionalPart (@ opt = true; @) before Part (@ ClassName classname = host.get_classname(); PartName partname = host.get_partname(); String indent = " "; if (tg.hasConstructionEdge(source, partname, classname)) { if (host.isDerived()) { edge_calls += indent + classname + " " + partname + " = get_" + partname + "();\n"; } if (classname.isBuiltinType()) { // Builtin types can't be compared with null. opt = false; } if (opt) edge_calls += indent + "if (" + partname + " != null) {\n"; Glob glob = new PartGlob(source, partname, classname); String i = indent + (opt ? " " : ""); if (host.isTerminal()) { edge_calls += parms.callBefores(glob, i) + revparms.callAfters(glob, i); } else { String call = ""; if (!use_nodeset) { call += parms.callBefores(glob, i); call += i + partname + "." + name + "(" + args + ");\n"; call += revparms.callAfters(glob, i); } else { call +=i+"{ java.util.BitSet newnodes = new java.util.BitSet();\n"; String ii = i + " "; call += tg.cedgeMaskCode(source, partname, classname, ii, "nodes", "newnodes"); call += ii + "if (!newnodes.equals(new java.util.BitSet())) {\n"; String iii = ii + " "; call += parms.callBefores(glob, iii); call += iii + partname + "." + name + "(newnodes"; if (!args.equals("")) call += ", " + args; call += ");\n"; call += revparms.callAfters(glob, iii); call += ii + "}\n"; call += i + "}\n"; } edge_calls += def.maybeAddAroundMethod(name, parms, glob, call, parms.callArounds(glob, i, source, name, args_with_nodeset), use_nodeset); } if (opt) edge_calls += indent + "}\n"; } @) after OptionalPart (@ opt = false; @) before Superclass (@ ClassName sup = host.get_classname(); if (tg.hasInheritanceEdge(source, sup) && Program.prog.definesClass(sup)) { Glob glob = new SuperclassGlob(source, sup); String i = " "; String call = ""; if (!use_nodeset) { call += parms.callBefores(glob, i); call += i + "super." + name + "(" + args + ");\n"; call += revparms.callAfters(glob, i); } else { call += i+"{ java.util.BitSet newnodes = new java.util.BitSet();\n"; String ii = i + " "; call += tg.iedgeMaskCode(source, sup, ii, "nodes", "newnodes"); call += ii + "if (!newnodes.equals(new java.util.BitSet())) {\n"; String iii = ii + " "; call += parms.callBefores(glob, iii); call += iii + "super." + name + "(newnodes"; if (!args.equals("")) call += ", " + args; call += ");\n"; call += revparms.callAfters(glob, iii); call += ii + "}\n"; call += i + "}\n"; } edge_calls += def.maybeAddAroundMethod(name, parms, glob, call, parms.callArounds(glob, i, source, name, args_with_nodeset), use_nodeset); } @) before Subclass (@ ClassName subclass = host.get_classname(); if (!tg.hasVertex(subclass)) { // The subclass is not in the traversal; generate an empty // traversal method, so that the parent's method doesn't get // inherited. ClassDef def = Program.prog.findClassDef(subclass); if (def != null) def.addTraversalMethod(name, parms, "", use_nodeset); } @) } } TraversalMethodName { (@ TraversalMethodName(ClassName source, TraversalName name) { methodname = MethodName.parse(name + "_" + source + "_trv"); } TraversalMethodName(String name) { methodname = MethodName.parse(name); } @) protected String withSuffix(String suffix) (@ return methodname + "_" + suffix; @) String beforeName() (@ return withSuffix("bef"); @) String afterName() (@ return withSuffix("aft"); @) String aroundName(Glob glob, ClassName clname) (@ return withSuffix("aro_" + clname + glob.methodSuffix()); @) } ClassDef { private String traversalMethodSignature(String name, TraversalParms parms, boolean use_nodeset) (@ String signature = " void " + name + "("; if (use_nodeset) signature += "java.util.BitSet nodes"; Visitor_Commalist vis = parms.get_visitors(); if (vis != null) { if (use_nodeset) signature += ", "; signature += vis; } signature += ")"; return signature; @) void addTraversalMethod(TraversalMethodName name, TraversalParms parms, String body, boolean use_nodeset) (@ String sig = traversalMethodSignature(name.toString(), parms, use_nodeset); addMethod(sig, body); @) void addBeforeMethod(TraversalMethodName name, TraversalParms parms, String body) (@ addMethod(" void " + name.beforeName() + parms, body); @) void addAfterMethod(TraversalMethodName name, TraversalParms parms, String body) (@ addMethod(" void " + name.afterName() + parms, body); @) void addAroundMethod(TraversalMethodName name, TraversalParms parms, Glob glob, String body, boolean use_nodeset) (@ ClassName clname = get_classname(); String mname = name.aroundName(glob, clname); String sig = traversalMethodSignature(mname, parms, use_nodeset); addMethod(sig, body); String s = ""; s +=" static java.lang.reflect.Method " + mname + ";\n" + " static {\n" + " try {\n" + " " + mname + " =\n" + " " + clname + ".class.getDeclaredMethod(\"" + mname + "\",\n" + " new Class[] { "; Enumeration e = parms.elements(); boolean first = true; if (use_nodeset) { s += "java.util.BitSet.class"; first = false; } while (e.hasMoreElements()) { Visitor vis = (Visitor) e.nextElement(); if (first) first = false; else s += ", "; s += vis.get_classname() + ".class"; } s += " });\n"; s += " } catch (NoSuchMethodException e) {\n"; s += " throw new RuntimeException(e.toString());\n"; s += " }\n"; s += " }\n"; addMethod(s); @) String maybeAddAroundMethod(TraversalMethodName name, TraversalParms parms, Glob glob, String edge_call, String around_call, boolean use_nodeset) (@ if (around_call.equals("")) return edge_call; // What if more than one method?? addAroundMethod(name, parms, glob, edge_call, use_nodeset); return around_call; @) }