예제 #1
0
        private Node CreateTypeDefInstance(Dictionary <string, ParserNode> typeDefMap, List <BuildError> errors)
        {
            // Find the TypeDef parser node.
            if (!typeDefMap.TryGetValue(Tag, out var parserDef))
            {
                errors.Add(ParserErrorHelper.MissingTypeDefInstance(Tag, File));
                return(null);
            }

            // Create an instance from the TypeDef parser node.
            var instance = parserDef.CreateInstance(typeDefMap, errors);

            if (instance == null)
            {
                return(null);
            }

            // Set manager and guid.
            instance.Guid     = Guid;
            instance.NodeType = Tag;

            // Set function values.
            foreach (var function in ScriptedFunctions)
            {
                function.SetFunctionPropertyValue(instance);
            }

            if (instance.Children == null)
            {
                instance.Children = new List <Node>();
            }

            return(instance);
        }
예제 #2
0
        private Dictionary <string, XmlDocument> LoadDocuments(string[] files, List <BuildError> errors)
        {
            var documents = new Dictionary <string, XmlDocument>();

            foreach (var file in files)
            {
                // Check file exists.
                if (!File.Exists(file))
                {
                    errors.Add(ParserErrorHelper.FileMissing(file));
                    continue;
                }

                // Try load file into xml.
                XmlDocument xmlDocument;
                try { xmlDocument = LoadFromFile(file); }
                catch (Exception e)
                {
                    errors.Add(ParserErrorHelper.FileLoad(file, e.Message));
                    continue;
                }

                documents[file] = xmlDocument;
            }

            return(documents);
        }
예제 #3
0
        private static bool TryResolveTypes(List <ParserNode> parserNodes, Dictionary <string, ParserNode> typeDefMap,
                                            List <BuildError> errors)
        {
            var criticalError = false;

            // Determine TypeDef for each node.
            foreach (var node in parserNodes)
            {
                if (!node.IsDerivedFromTypeDef)
                {
                    continue;
                }

                if (typeDefMap.TryGetValue(node.Tag, out var typeDef))
                {
                    node.TypeDef = typeDef;
                }
                else
                {
                    errors.Add(ParserErrorHelper.MissingTypeDef(node.Tag, node.File));
                    criticalError = true;
                }
            }

            // Return early on critical errors.
            if (criticalError)
            {
                return(false);
            }

            // Resolve underlying types for all nodes derived from a TypeDef.
            // One TypeDef can be derived from another TypeDef, which means we must resolve them with recursion (or loop).
            var open = parserNodes
                       .Where(n => n.Type == null)
                       .ToList();

            while (open.Count > 0)
            {
                var next = open.FirstOrDefault(n => n.TypeDef.Type != null);

                if (next == null)
                {
                    errors.Add(ParserErrorHelper.TypeDefsUnresolved(open.Select(n => n.Tag)));
                    return(false);
                }

                next.Type = next.TypeDef.Type;
                open.Remove(next);
            }

            return(true);
        }
예제 #4
0
        private Node CreateStaticTypeInstance(List <BuildError> errors)
        {
            // Create an instance using reflection.
            Node instance;

            try
            {
                var serializer = new XmlSerializer(Type);
                instance = serializer.Deserialize(new XmlNodeReader(Xml)) as Node;
            }
            catch (Exception e)
            {
                errors.Add(ParserErrorHelper.UnableToInstantiate(Tag, File, e.Message));
                return(null);
            }

            if (instance == null)
            {
                errors.Add(ParserErrorHelper.CannotCastToNode(Tag, File));
                return(null);
            }

            // Set manager and guid.
            instance.Guid = Guid;

            // Set function values.
            foreach (var function in ScriptedFunctions)
            {
                function.SetFunctionPropertyValue(instance);
            }

            if (instance.Children == null)
            {
                instance.Children = new List <Node>();
            }

            return(instance);
        }
예제 #5
0
        private void Expand(string file, XmlDocument document, Dictionary <string, ParserNode> typeDefMap,
                            List <BuildError> errors, Dictionary <string, ParserNode> guids, HashSet <string> nodeTags)
        {
            var rootXml = document.SelectSingleNode("/__Main");
            var root    = new ParserNode {
                Xml = rootXml, Tag = "__Main", File = file
            };
            var open = new Stack <ParserNode>();

            open.Push(root);

            while (open.Count > 0)
            {
                var next = open.Pop();
                if (!next.Xml.HasChildNodes)
                {
                    continue;
                }

                next.Children = new List <ParserNode>();

                foreach (var xmlChild in GetChildren(next.Xml).ToList())
                {
                    if (xmlChild.NodeType != XmlNodeType.Element)
                    {
                        continue;
                    }

                    // Determine if the element is a behaviour node.
                    var tag = xmlChild.Name;
                    if (!nodeTags.Contains(tag))
                    {
                        continue;
                    }

                    // Remove this child element from the parent's xml.
                    next.Xml.RemoveChild(xmlChild);

                    // Determine its guid. Check repetitions. Remove guid attribute from the xml.
                    var guidAttribute = GetAttribute(xmlChild, GuidAttribute);
                    var guid          = guidAttribute?.Value;
                    if (guidAttribute != null)
                    {
                        xmlChild.Attributes?.Remove(guidAttribute);
                    }

                    if (string.IsNullOrWhiteSpace(guid))
                    {
                        guid = GetRandomGuid(guids);
                    }
                    else if (guids.TryGetValue(guid, out var prev))
                    {
                        errors.Add(ParserErrorHelper.RepeatedNodeGuid(guid, file, prev.File));
                        return;
                    }

                    // Search for a type definition attribute on the node. Remove typedef attribute from the xml.
                    var typeDefAttriute = GetAttribute(xmlChild, TypeDefAttribute);
                    var typeDef         = typeDefAttriute?.Value;
                    if (typeDefAttriute != null)
                    {
                        xmlChild.Attributes?.Remove(typeDefAttriute);
                    }

                    // Check TypeDef repetitions.
                    if (!string.IsNullOrWhiteSpace(typeDef) && typeDefMap.TryGetValue(typeDef, out var prevTypeDef))
                    {
                        errors.Add(ParserErrorHelper.RepeatedTypeDef(typeDef, file, prevTypeDef.File));
                        return;
                    }

                    // Create the parser node.
                    var n = new ParserNode
                    {
                        Xml              = xmlChild,
                        Tag              = tag,
                        File             = file,
                        Guid             = guid,
                        DeclaringTypeDef = typeDef,
                        IsTopmost        = next == root
                    };

                    // Try resolve the node type from a compiled type.
                    if (BaseNodeMap.TryGetValue(tag, out var type))
                    {
                        n.Type = type;
                        n.IsDerivedFromTypeDef = false;
                    }
                    else
                    {
                        n.IsDerivedFromTypeDef = true;
                    }

                    guids[guid] = n;
                    if (!string.IsNullOrWhiteSpace(typeDef))
                    {
                        typeDefMap[typeDef] = n;
                    }

                    next.Children.Add(n);
                    open.Push(n);
                }
            }
        }
예제 #6
0
        public YggCompilation Compile <TState>(IEnumerable <string> namespaces,
                                               IEnumerable <string> referenceAssemblyPaths,
                                               List <ScriptedFunctionDefinition> definitions)
        {
            var compilation      = new YggCompilation();
            var builderClassText = new StringBuilder();
            var usings           = new List <string>(namespaces.Distinct().Select(s => $"using {s};\n"));
            var referencePaths   = new HashSet <string>(referenceAssemblyPaths);

            // Add dynamic using if necessary.
            if (definitions.Any(d => d.ReplaceObjectWithDynamic))
            {
                usings.Add("using System.Dynamic;");
            }

            foreach (var u in usings)
            {
                builderClassText.Append(u);
            }

            builderClassText.Append("public class FunctionBuilder\n{");

            foreach (var definition in definitions)
            {
                var sf = CreateScriptedFunction <TState>(definition.Guid, definition.FunctionProperty,
                                                         definition.FunctionText, definition.ReplaceObjectWithDynamic, compilation.Errors);

                if (sf == null)
                {
                    continue;
                }

                if (!compilation.GuidFunctionMap.TryGetValue(sf.Guid, out var functions))
                {
                    functions = new List <ScriptedFunction>();
                    compilation.GuidFunctionMap[sf.Guid] = functions;
                }

                functions.Add(sf);
                builderClassText.Append(sf.ScriptText);

                foreach (var reference in sf.References)
                {
                    referencePaths.Add(reference);
                }
            }

            builderClassText.Append("\n}");

            List <PortableExecutableReference> references;

            try
            {
                references = referencePaths
                             .Select(p => MetadataReference.CreateFromFile(p))
                             .ToList();
            }
            catch (Exception e)
            {
                compilation.Errors.Add(ParserErrorHelper.CannotLoadReference(e.Message));
                return(compilation);
            }

            var options = ScriptOptions.Default.AddReferences(references);
            var script  = CSharpScript.Create(builderClassText.ToString(), options);
            var comp    = script.GetCompilation();

            byte[] compiledAssembly;
            using (var output = new MemoryStream())
            {
                var emitResult = comp.Emit(output);

                if (!emitResult.Success)
                {
                    var error = new BuildError {
                        Message = "Emit compilation error.", IsCritical = true
                    };
                    foreach (var diag in emitResult.Diagnostics)
                    {
                        error.Diagnostics.Add(diag);
                    }

                    compilation.Errors.Add(error);
                    return(compilation);
                }

                compiledAssembly = output.ToArray();
            }

            var assembly  = Assembly.Load(compiledAssembly);
            var entryType = assembly.GetTypes().First(t => t.Name == "FunctionBuilder");
            var builder   = Activator.CreateInstance(entryType);

            foreach (var sf in compilation.GuidFunctionMap.Values.SelectMany(g => g))
            {
                sf.Builder       = builder;
                sf.BuilderMethod = entryType.GetMethod(sf.BuilderMethodName);
            }

            return(compilation);
        }
예제 #7
0
        private static ScriptedFunction CreateScriptedFunction <TState>(string guid, PropertyInfo property,
                                                                        string functionText, bool replaceObjectWithDynamic, List <BuildError> errors)
        {
            var propertyName          = property.Name;
            var builderName           = GetFunctionName("B", guid, propertyName);
            var functionName          = GetFunctionName("F", guid, propertyName);
            var functionType          = property.PropertyType;
            var genericTypeDefinition = functionType.GetGenericTypeDefinition();
            var hasReturnStatement    = _returnStatement.IsMatch(functionText);
            var returnOpenText        = hasReturnStatement ? string.Empty : "return ";
            var returnCloseText       = hasReturnStatement ? string.Empty : ";";
            var stateType             = typeof(TState);
            var stateTypeName         = stateType.FullName?.Replace("+", ".");
            var generics          = property.PropertyType.GetGenericArguments();
            var firstGenericType  = generics.Length > 0 ? generics[0] : null;
            var firstGenericName  = firstGenericType?.FullName?.Replace("+", ".");
            var secondGenericType = generics.Length > 1 ? generics[1] : null;
            var secondGenericName = secondGenericType?.FullName?.Replace("+", ".");
            var isSameType        = firstGenericType == stateType;

            if (!_supportedScriptedFunctionTypes.Contains(functionType) &&
                !_supportedScriptedFunctionTypes.Contains(genericTypeDefinition))
            {
                var error = ParserErrorHelper.UnsupportedScriptFunctionType(guid, property.DeclaringType?.Name,
                                                                            functionType.Name, genericTypeDefinition.Name, functionText);

                errors.Add(error);
                return(null);
            }

            var sf = new ScriptedFunction();

            sf.Guid               = guid;
            sf.PropertyName       = propertyName;
            sf.BuilderMethodName  = builderName;
            sf.FunctionMethodName = functionName;
            sf.FunctionText       = functionText;
            sf.Property           = property;

            // References.
            sf.References.Add(stateType.GetTypeInfo().Assembly.Location);
            if (firstGenericType != null)
            {
                sf.References.Add(firstGenericType.GetTypeInfo().Assembly.Location);
            }

            if (secondGenericType != null)
            {
                sf.References.Add(secondGenericType.GetTypeInfo().Assembly.Location);
            }

            if (replaceObjectWithDynamic)
            {
                sf.References.Add(typeof(RuntimeBinderException).GetTypeInfo().Assembly.Location);
                sf.References.Add(typeof(DynamicAttribute).GetTypeInfo().Assembly.Location);

                if (firstGenericName != null && firstGenericType == typeof(object))
                {
                    firstGenericName = "dynamic";
                }

                if (secondGenericName != null && secondGenericType == typeof(object))
                {
                    secondGenericName = "dynamic";
                }
            }

            // Action with single generic.
            if (functionType.IsGenericType && genericTypeDefinition == typeof(Action <>))
            {
                sf.ScriptText = isSameType
                    ? $@"public static void {functionName}({firstGenericName} state) {{ {functionText}; }} 
                         public System.Action<{firstGenericName}> {builderName}() {{ return {functionName}; }} "
                    : $@"public static void {functionName}({firstGenericName} baseState) {{ var state = ({stateTypeName})baseState; {functionText}; }} 
                         public System.Action<{firstGenericName}> {builderName}() {{ return {functionName}; }} ";

                return(sf);
            }

            // Function with single generic.
            if (functionType.IsGenericType && genericTypeDefinition == typeof(Func <>))
            {
                sf.ScriptText =
                    $@"public static {firstGenericName} {functionName}() {{ {returnOpenText}{functionText}{returnCloseText} }} 
                                   public System.Func<{firstGenericName}> {builderName}() {{ return {functionName}; }}";

                return(sf);
            }

            // Function with double generic.
            if (functionType.IsGenericType && genericTypeDefinition == typeof(Func <,>))
            {
                sf.ScriptText = isSameType
                    ? $@"public static {secondGenericName} {functionName}({firstGenericName} state) {{ {returnOpenText}{functionText}{returnCloseText} }} 
                         public System.Func<{firstGenericName}, {secondGenericName}> {builderName}() {{ return {functionName}; }}"
                    : $@"public static {secondGenericName} {functionName}({firstGenericName} baseState) {{ var state = ({stateTypeName})baseState; {returnOpenText}{functionText}{returnCloseText} }} 
                         public System.Func<{firstGenericName}, {secondGenericName}> {builderName}() {{ return {functionName}; }}";

                return(sf);
            }

            return(null);
        }