Пример #1
0
        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);
        }
Пример #2
0
        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();
                }
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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));
        }
Пример #5
0
        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);
        }
Пример #7
0
        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());
        }