private void CheckWellFormedness(UnionOrComposeModuleExpr composeExpr) { if (composeExpr.ModuleInfo != null) { return; } //check that all component modules are wellformed foreach (var module in composeExpr.ComponentModules) { CheckWellFormedness(module); } //check if the current module is wellformed // TODO: Woah, this is O(n^2). Can we get this down to O(n log n) at most? foreach (var module1 in composeExpr.ComponentModules) { foreach (var module2 in composeExpr.ComponentModules) { if (module1 == module2) { continue; } var module1Info = module1.ModuleInfo; var module2Info = module2.ModuleInfo; var allPrivateEvents = module1Info .PrivateEvents.Events .Union(module2Info.PrivateEvents.Events).ToList(); var allSendAndReceiveEvents = module1Info.Sends.Events.Union( module1Info.Receives.Events.Union( module2Info.Receives.Events.Union( module2Info.Sends.Events))).ToList(); // 1) domain of interface def map is disjoint foreach (var @interface in module1Info.InterfaceDef.Keys.Intersect( module2Info.InterfaceDef.Keys)) { throw handler.InvalidCompositionExpr(module1.SourceLocation, "bound interfaces after composition are not disjoint, e.g., " + $"interface {@interface.Name} is bound in both the modules being composed"); } // 2) no private events in the sends or receives events foreach (var @event in allSendAndReceiveEvents.Intersect(allPrivateEvents)) { throw handler.InvalidCompositionExpr(module1.SourceLocation, "private events after composition are not disjoint from send and receives set, e.g., " + $"after composition private event {@event.Name} belongs to both private and public (sends or receives) events"); } // 3) no private events in the sends or receives permissions foreach (var @event in allSendAndReceiveEvents) { var permissionsEmbedded = GetPermissions(@event.PayloadType.AllowedPermissions?.Value); foreach (var privatePermission in allPrivateEvents.Where( ev => permissionsEmbedded.Contains(ev))) { throw handler.InvalidCompositionExpr(module1.SourceLocation, "private events after composition are not disjoint from permissions in events sent or received, e.g., " + $"after composition private event {privatePermission.Name} is in the permissions set of {@event.Name}"); } } var interfaceImplAndNotCreated1 = module1Info.Creates.Interfaces.Except(module1Info.InterfaceDef.Keys); var interfaceCreatedAndNotImpl1 = module1Info.InterfaceDef.Keys.Except(module1Info.Creates.Interfaces); var interfaceImplAndNotCreated2 = module2Info.Creates.Interfaces.Except(module2Info.InterfaceDef.Keys); var interfaceCreatedAndNotImpl2 = module2Info.InterfaceDef.Keys.Except(module2Info.Creates.Interfaces); foreach (var @interface in interfaceImplAndNotCreated1.Union( interfaceCreatedAndNotImpl1.Union( interfaceImplAndNotCreated2.Union(interfaceCreatedAndNotImpl2)))) { foreach (var @event in allPrivateEvents.Where( ev => @interface.ReceivableEvents.Contains(ev))) { throw handler.InvalidCompositionExpr(module1.SourceLocation, $"After composition, private event {@event.Name} is in the received events of interface {@interface.Name} which is created or bound in the module"); } } // ensure also that the monitor maps are disjoint foreach (var monitor in module1Info.MonitorMap.Keys.Intersect(module2Info.MonitorMap.Keys)) { throw handler.InvalidCompositionExpr(module1.SourceLocation, $"monitor {monitor.Name} is attached in more than one modules being composed"); } // if composition then output actions must be disjoint if (composeExpr.IsComposition) { foreach (var @event in module1Info.Sends.Events.Intersect(module2Info.Sends.Events)) { throw handler.InvalidCompositionExpr(module1.SourceLocation, $"output sends are not disjoint, {@event.Name} belongs to the sends of multiple composed module"); } foreach (var @interface in module1Info.Creates.Interfaces.Intersect( module2Info.Creates.Interfaces)) { throw handler.InvalidCompositionExpr(module1.SourceLocation, $"output creates are not disjoint, {@interface.Name} belongs to the creates of multiple composed module"); } } foreach (var exportedOrCreatedInterface in module1.ModuleInfo.InterfaceDef.Keys.Union(module1.ModuleInfo .Creates.Interfaces)) { foreach (var priEvent in module2.ModuleInfo.PrivateEvents.Events.Where(ev => GetPermissions(exportedOrCreatedInterface.PayloadType.AllowedPermissions?.Value).Contains(ev))) { throw handler.InvalidHideEventExpr(module2.SourceLocation, $"private event {priEvent.Name} belongs to the permissions of the constructor type of public interface {exportedOrCreatedInterface.Name}"); } } } } composeExpr.ModuleInfo = new ModuleInfo(); var currentModuleInfo = composeExpr.ModuleInfo; //populate the attributes of the module foreach (var module in composeExpr.ComponentModules.Select(cm => cm.ModuleInfo)) { currentModuleInfo.PrivateEvents.AddEvents(module.PrivateEvents.Events); currentModuleInfo.PrivateInterfaces.AddInterfaces(module.PrivateInterfaces.Interfaces); foreach (var monMap in module.MonitorMap) { currentModuleInfo.MonitorMap[monMap.Key] = monMap.Value.ToList(); } foreach (var linkMapItem in module.LinkMap) { currentModuleInfo.LinkMap[linkMapItem.Key] = new Dictionary <Interface, Interface>(); foreach (var localLinkMap in linkMapItem.Value) { currentModuleInfo.LinkMap[linkMapItem.Key].Add(localLinkMap.Key, localLinkMap.Value); } } foreach (var ipItem in module.InterfaceDef) { currentModuleInfo.InterfaceDef.Add(ipItem.Key, ipItem.Value); } } // compute all the derived attributes currentModuleInfo.Sends.AddEvents(composeExpr.ComponentModules.SelectMany(m => m.ModuleInfo.Sends.Events)); currentModuleInfo.Receives.AddEvents( composeExpr.ComponentModules.SelectMany(m => m.ModuleInfo.Receives.Events)); currentModuleInfo.Creates.AddInterfaces( composeExpr.ComponentModules.SelectMany(m => m.ModuleInfo.Creates.Interfaces)); }