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); }
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); }
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); }
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); }
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); } } }
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); }
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); }