package org.scribble.main; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.scribble.ast.Module; import org.scribble.codegen.java.endpointapi.SessionApiGenerator; import org.scribble.codegen.java.endpointapi.StateChannelApiGenerator; import org.scribble.codegen.java.endpointapi.ioifaces.IOInterfacesGenerator; import org.scribble.del.local.LProtocolDeclDel; import org.scribble.sesstype.name.GProtocolName; import org.scribble.sesstype.name.LProtocolName; import org.scribble.sesstype.name.ModuleName; import org.scribble.sesstype.name.Role; import org.scribble.visit.AstVisitor; import org.scribble.visit.InlinedProtocolUnfolder; import org.scribble.visit.ProtocolDefInliner; import org.scribble.visit.context.ModuleContextBuilder; import org.scribble.visit.context.ProjectedChoiceDoPruner; import org.scribble.visit.context.ProjectedChoiceSubjectFixer; import org.scribble.visit.context.ProjectedRoleDeclFixer; import org.scribble.visit.context.Projector; import org.scribble.visit.context.ProtocolDeclContextBuilder; import org.scribble.visit.util.RoleCollector; import org.scribble.visit.validation.GProtocolValidator; import org.scribble.visit.wf.DelegationProtocolRefChecker; import org.scribble.visit.wf.ExplicitCorrelationChecker; import org.scribble.visit.wf.NameDisambiguator; import org.scribble.visit.wf.ReachabilityChecker; import org.scribble.visit.wf.WFChoiceChecker; // A "compiler job" front-end that supports operations comprising one or more visitor passes over the AST public class Job { // FIXME: verbose/debug printing parameter: should be in MainContext, but currently cannot access that class directly from here //public final boolean jUnit; public final boolean debug; public final boolean useOldWf; public final boolean noProgress; // FIXME: deprecate public final boolean minEfsm; // Currently only affects EFSM output (i.e. -fsm, -dot) and API gen -- doesn't affect model checking public final boolean fair; public final boolean noLocalChoiceSubjectCheck; public final boolean noAcceptCorrelationCheck; public final boolean noValidation; private final JobContext jcontext; // Mutable (Visitor passes replace modules) // Just take MainContext as arg? -- would need to fix Maven dependencies //public Job(boolean jUnit, boolean debug, Map<ModuleName, Module> parsed, ModuleName main, boolean useOldWF, boolean noLiveness) public Job(boolean debug, Map<ModuleName, Module> parsed, ModuleName main, boolean useOldWF, boolean noLiveness, boolean minEfsm, boolean fair, boolean noLocalChoiceSubjectCheck, boolean noAcceptCorrelationCheck, boolean noValidation) { //this.jUnit = jUnit; this.debug = debug; this.useOldWf = useOldWF; this.noProgress = noLiveness; this.minEfsm = minEfsm; this.fair = fair; this.noLocalChoiceSubjectCheck = noLocalChoiceSubjectCheck; this.noAcceptCorrelationCheck = noAcceptCorrelationCheck; this.noValidation = noValidation; this.jcontext = new JobContext(this, parsed, main); // Single instance per Job and should never be shared } public void checkWellFormedness() throws ScribbleException { runContextBuildingPasses(); runUnfoldingPass(); runWellFormednessPasses(); } public void runContextBuildingPasses() throws ScribbleException { runVisitorPassOnAllModules(ModuleContextBuilder.class); // Always done first (even if other contexts are built later) so that following passes can use ModuleContextVisitor runVisitorPassOnAllModules(NameDisambiguator.class); // Includes validating names used in subprotocol calls.. runVisitorPassOnAllModules(ProtocolDeclContextBuilder.class); //..which this pass depends on. This pass basically builds protocol dependency info runVisitorPassOnAllModules(DelegationProtocolRefChecker.class); // Must come after ProtocolDeclContextBuilder runVisitorPassOnAllModules(RoleCollector.class); // Actually, this is the second part of protocoldecl context building runVisitorPassOnAllModules(ProtocolDefInliner.class); //runUnfoldingPass(); } // "Second part" of context building (separated for extensions to work on non-unfolded protos) public void runUnfoldingPass() throws ScribbleException { runVisitorPassOnAllModules(InlinedProtocolUnfolder.class); } public void runWellFormednessPasses() throws ScribbleException { if (!this.noValidation) { runVisitorPassOnAllModules(WFChoiceChecker.class); // For enabled roles and disjoint enabling messages -- includes connectedness checks runProjectionPasses(); runVisitorPassOnAllModules(ReachabilityChecker.class); // Moved before GlobalModelChecker.class, OK? if (!this.useOldWf) { runVisitorPassOnAllModules(GProtocolValidator.class); } } } // Due to Projector not being a subprotocol visitor, so "external" subprotocols may not be visible in ModuleContext building for the projections of the current root Module // SubprotocolVisitor it doesn't visit the target Module/ProtocolDecls -- that's why the old Projector maintained its own dependencies and created the projection modules after leaving a Do separately from SubprotocolVisiting // So Projection should not be an "inlining" SubprotocolVisitor, it would need to be more a "DependencyVisitor" private void runProjectionPasses() throws ScribbleException { runVisitorPassOnAllModules(Projector.class); runProjectionContextBuildingPasses(); if (!noAcceptCorrelationCheck) { runVisitorPassOnParsedModules(ExplicitCorrelationChecker.class); } } // To be done as a barrier pass after projection done on all Modules -- N.B. Module context building, no other validation (so "fixing" can be done in following passes) // Also does projection "fixing" (choice subjects, subprotocol roledecls) private void runProjectionContextBuildingPasses() throws ScribbleException { runVisitorPassOnProjectedModules(ModuleContextBuilder.class); runVisitorPassOnProjectedModules(ProtocolDeclContextBuilder.class); runVisitorPassOnProjectedModules(RoleCollector.class); // NOTE: doesn't collect from choice subjects (may be invalid until projected choice subjs fixed) runVisitorPassOnProjectedModules(ProjectedChoiceDoPruner.class); if (!this.noLocalChoiceSubjectCheck) { // Disabling ProjectedChoiceSubjectFixer (local choice subject inference) goes towards a general global WF, but is currently unsound runVisitorPassOnProjectedModules(ProjectedChoiceSubjectFixer.class); // Must come before other passes that need DUMMY role occurrences to be fixed } runVisitorPassOnProjectedModules(ProjectedRoleDeclFixer.class); // Possibly could do after inlining, and do role collection on the inlined version runVisitorPassOnProjectedModules(ProtocolDefInliner.class); runVisitorPassOnProjectedModules(InlinedProtocolUnfolder.class); } // Pre: checkWellFormedness // Returns: full proto name -> Module public Map<LProtocolName, Module> getProjections(GProtocolName fullname, Role role) throws ScribbleException { Module root = this.jcontext.getProjection(fullname, role); Map<LProtocolName, Set<Role>> dependencies = ((LProtocolDeclDel) root.getLocalProtocolDecls().get(0).del()) .getProtocolDeclContext().getDependencyMap().getDependencies().get(role); // Can ignore Set<Role> for projections (is singleton), as each projected proto is a dependency only for self (implicit in the protocoldecl) return dependencies.keySet().stream().collect( Collectors.toMap((lpn) -> lpn, (lpn) -> this.jcontext.getModule(lpn.getPrefix()))); } public Map<String, String> generateSessionApi(GProtocolName fullname) throws ScribbleException { debugPrintPass("Running " + SessionApiGenerator.class + " for " + fullname); SessionApiGenerator sg = new SessionApiGenerator(this, fullname); Map<String, String> map = sg.generateApi(); // filepath -> class source return map; } // FIXME: refactor an EndpointApiGenerator -- ? public Map<String, String> generateStateChannelApi(GProtocolName fullname, Role self, boolean subtypes) throws ScribbleException { /*if (this.jcontext.getEndpointGraph(fullname, self) == null) { buildGraph(fullname, self); }*/ debugPrintPass("Running " + StateChannelApiGenerator.class + " for " + fullname + "@" + self); StateChannelApiGenerator apigen = new StateChannelApiGenerator(this, fullname, self); IOInterfacesGenerator iogen = null; try { iogen = new IOInterfacesGenerator(apigen, subtypes); } catch (RuntimeScribbleException e) // FIXME: use IOInterfacesGenerator.skipIOInterfacesGeneration { //System.err.println("[Warning] Skipping I/O Interface generation for protocol featuring: " + fullname); warningPrintln("Skipping I/O Interface generation for: " + fullname + "\n Cause: " + e.getMessage()); } // Construct the Generators first, to build all the types -- then call generate to "compile" all Builders to text (further building changes will not be output) Map<String, String> api = new HashMap<>(); // filepath -> class source // Store results? api.putAll(apigen.generateApi()); if (iogen != null) { api.putAll(iogen.generateApi()); } return api; } public void runVisitorPassOnAllModules(Class<? extends AstVisitor> c) throws ScribbleException { debugPrintPass("Running " + c + " on all modules:"); runVisitorPass(this.jcontext.getFullModuleNames(), c); } public void runVisitorPassOnParsedModules(Class<? extends AstVisitor> c) throws ScribbleException { debugPrintPass("Running " + c + " on parsed modules:"); runVisitorPass(this.jcontext.getParsedFullModuleNames(), c); } public void runVisitorPassOnProjectedModules(Class<? extends AstVisitor> c) throws ScribbleException { debugPrintPass("Running " + c + " on projected modules:"); runVisitorPass(this.jcontext.getProjectedFullModuleNames(), c); } private void runVisitorPass(Set<ModuleName> modnames, Class<? extends AstVisitor> c) throws ScribbleException { try { Constructor<? extends AstVisitor> cons = c.getConstructor(Job.class); for (ModuleName modname : modnames) { AstVisitor nv = cons.newInstance(this); runVisitorOnModule(modname, nv); } } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } private void runVisitorOnModule(ModuleName modname, AstVisitor nv) throws ScribbleException { Module visited = (Module) this.jcontext.getModule(modname).accept(nv); this.jcontext.replaceModule(visited); } public JobContext getContext() { return this.jcontext; } public boolean isDebug() { return this.debug; } public void warningPrintln(String s) { System.err.println("[Warning] " + s); } public void debugPrintln(String s) { if (this.debug) { System.out.println(s); } } private void debugPrintPass(String s) { debugPrintln("\n[Step] " + s); } }