protected override bool Init(GenerationEnvironment env) { env.errorContext.ClearFlag(); if (!(base.Init(env))) { return(false); } symbol.TryGetAttribute(RelevantSymbols.AutoActivationAttribute, out var autoActivation); var exportedChains = GetExportedChains(env); if (autoActivation is null) { Chains = exportedChains.ToArray(); if (Chains.Length == 0) { env.ReportError("Behaviors must define at least a chain. Otherwise, they are just components"); } } else { if (exportedChains.Any()) { env.ReportError($"Behaviors decorated with AutoActivation cannot define additional chains."); } else { if (!TryInitActivationContext(env, out var context)) { return(false); } Chains = new IChainWrapper[] { new BehaviorActivationChainWrapper("Check", symbol, context), new BehaviorActivationChainWrapper("Do", symbol, context) }; foreach (var chain in Chains) { env.TryAddChain(chain); } ActivationAlias = autoActivation.Alias; if (env.aliases.Contains(ActivationAlias)) { env.ReportError($"Duplicate alias name {ActivationAlias} in behavior {symbol.Name}."); } } } return(!env.errorContext.Flag); }
public IEnumerable <ExportedMethodSymbolWrapper> GetNonNativeExportedMethods(GenerationEnvironment env) { foreach (var method in GetMethods()) { if (method.TryGetExportAttribute(out var attribute)) { env.errorContext.PushThing(method); if (attribute.Chain != null) { var m = new ExportedMethodSymbolWrapper(method, attribute); if (m.TryInit(env)) { yield return(m); } } else { // For now, add this check here but it should probably be elsewhere // Report an error if the class is not a behavior. if (!(this is BehaviorSymbolWrapper)) { env.ReportError($"The class {ClassName} marked a method for export but did not specify the chain path. Note: one may omit the chain path only if the method being exported is inside a behavior class."); } env.errorContext.PopThing(); yield break; } env.errorContext.PopThing(); } } }
public static bool ValidateChainUidAgainst(this string uid, GenerationEnvironment env) { if (!ChainIdentifier.TryParse(uid, env.errorContext, out var parsed)) { return(false); } if (env.exportingClasses.TryGetValue(parsed.Class, out var exportingType)) { switch (parsed.Type) { case ChainContributionType.More: case ChainContributionType.Global: return(TrueOr( exportingType.contributedChains.Any(chain => chain.Name == parsed.Chain && chain.ContributionType == parsed.Type), () => env.ReportError($"{uid} references a non-existent chain: {parsed.Chain}."))); case ChainContributionType.Instance: if (exportingType is BehaviorSymbolWrapper behavior) { if (behavior.Chains.Any(chain => chain.Name == parsed.Chain)) { return(true); } env.ReportError($"{uid} references a non-existent behavior chain: {parsed.Chain}."); } else { env.ReportError($"{uid} referenced an exporting class that was not a behavior: {parsed.Class}. If you meant a static class, use '+{uid}' instead."); } return(false); default: return(false); } } else { env.ReportError($"{uid} references a non-existent exporting class: {parsed.Class}"); } return(false); }
public bool TryInitActivationContext(GenerationEnvironment env, out ContextSymbolWrapper context) { // Initialize the context class symbol wrapper var ctx_symbol = symbol.GetMembers().FirstOrDefault(s => s.Name == "Context"); if (ctx_symbol == null) { env.ReportError($"The {symbol.Name} behavior did not define a nested Context class.\nNote: Any behavior must define a Context class. If you do not have any chains in the behavior, make it a simple component. Behaviors by design differ from components in that they exploit chains."); context = default; return(false); } if (!(ctx_symbol is INamedTypeSymbol named_ctx_symbol)) { env.ReportError($"The Context defined inside {symbol.Name} must be a class"); context = default; return(false); } return(env.TryCacheContext(named_ctx_symbol, out context)); }
public AliasMethodSymbolWrapper[] GetAliasMethods(GenerationEnvironment env) { // Find aliases var aliasMethods = GetAliasMethodsHelper().ToArray(); // Add alias strings to global aliases foreach (var aliasMethod in aliasMethods) { if (!env.aliases.Add(aliasMethod.Alias)) { env.ReportError($"Aliases must be unique across all types. When processing the {symbol.Name} behavior, found a duplicate alias name: {aliasMethod.Alias}"); } } return(aliasMethods); }
protected override bool Init(GenerationEnvironment env) { if (symbol.HasAttribute(RelevantSymbols.InstanceExportAttribute.symbol)) { env.ReportError($"Components must not have the InstanceExportAttribute since its usage is ambiguous for components."); return(false); } if (base.Init(env)) { flaggedFields = GetFlaggedFields(); aliasMethods = GetAliasMethods(env); injectedFields = GetInjectedFields().ToArray(); HasInitInWorldMethod = symbol.GetMethods().Any(m => !m.IsStatic && m.Name == "InitInWorld"); return(env.TryAddExportingClass(this)); } return(false); }
public string GetAdapterBody(GenerationEnvironment env) { StringBuilder sb_params = new StringBuilder(); StringBuilder sb_call = new StringBuilder(); // If the method returns bool, it is treated toward propagation. if (SymbolEqualityComparer.Default.Equals(symbol.ReturnType, RelevantSymbols.boolType)) { sb_call.Append("ctx.Propagate = "); } sb_call.Append(GetNamePrefixAtCall()); sb_call.Append($"{Name}("); foreach (var s in symbol.Parameters) { // TODO: allow get properties if (s.Name == Context.ActorName) { sb_call.Append($"ctx.{Context.ActorName}, "); } // If the parameter is of Context type else if (SymbolEqualityComparer.Default.Equals(s.Type, Context.symbol)) { // The parameters need not be appended, since the handlers take ctx by default. sb_call.Append("ctx, "); } // if ctx class has a field of that name and type, reference it directly else if (Context.ContainsFieldWithNameAndType(s.Name, s.Type)) { if (s.RefKind == RefKind.None) { sb_params.AppendLine($"var _{s.Name} = ctx.{s.Name};"); sb_call.Append($"_{s.Name}, "); } else { sb_call.Append($"{s.RefKind.AsKeyword()} ctx.{s.Name}, "); } } // if it is of a component type, retrieve it from the entity else if (s.Type.HasInterface(RelevantSymbols.IComponent)) { // if the name contains the name of an entity type field // of the context followed by an underscore, get the component // from that entity and save it. int indexOf_ = s.Name.IndexOf('_'); bool success = false; if (indexOf_ != -1) { string entity_name = s.Name.Substring(0, indexOf_); if (Context.ContainsEntity(entity_name)) { success = true; sb_params.AppendLine($"var _{s.Name} = ctx.{entity_name}.Get{s.Type.Name}();"); sb_call.Append($"_{s.Name}, "); } else { // TODO: Report warning? } } if (!success) { // get the component from entity. For now, assume that // the entity is assumed to always contain the given component. sb_params.AppendLine($"var _{s.Name} = ctx.actor.Get{s.Type.Name}();"); sb_call.Append($"_{s.Name}, "); } } else { if (SymbolEqualityComparer.Default.Equals(s.Type, RelevantSymbols.entity)) { env.ReportError($"The entity must be named \"actor\", like on the context class"); return(null); } env.ReportError($"The name \"{s.Name}\" is invalid. It does not correspond directly to any of the Context fields and the type of the parameter was not a component type"); return(null); } } if (!symbol.Parameters.IsEmpty) { sb_call.Remove(sb_call.Length - 2, 2); } sb_call.Append(");"); sb_params.Append(sb_call.ToString()); return(sb_params.ToString()); }