예제 #1
0
 public SerializerMethodGenerator(TypeClassifier typeClassifier, SerializationMethodProviders customMethods,
                                  IEnumerable <Type> suplementalDispatchTypes)
 {
     this.typeClassifier           = typeClassifier;
     this.customMethods            = customMethods;
     this.suplementalDispatchTypes = suplementalDispatchTypes;
 }
예제 #2
0
 public TypeDiscovery(SerializationMethodProviders customMethods, IEnumerable <Assembly> initialAssemblies)
 {
     this.customMethods = customMethods;
     foreach (var a in initialAssemblies)
     {
         AddAssembly(a);                 // Pre-fill with initial assemblies
     }
 }
예제 #3
0
 internal GeneratorResult(SerializationMethodProviders serializationMethods,
                          Dictionary <Type, SerializeDispatchDelegate> dispatchTable,
                          DeserializeDispatchDelegate deserializeDispatch,
                          List <Module> moduleTable)
 {
     this.serializationMethods   = serializationMethods;
     this.serializeDispatchTable = dispatchTable;
     this.deserializeDispatch    = deserializeDispatch;
     this.moduleTable            = moduleTable;
 }
예제 #4
0
        public GeneratorResult Generate(MethodCreatorCreator methodCreatorCreator)
        {
            #region Declare Value Type Serializer Methods

            var valueTypeSerializerType   = methodCreatorCreator.Create("ValueTypeSerializer");
            var valueTypeDeserializerType = methodCreatorCreator.Create("ValueTypeDeserializer");

            var generatedValueTypeSerializeMethods   = new Dictionary <Type, MethodInfo>();
            var generatedValueTypeDeserializeMethods = new Dictionary <Type, MethodInfo>();

            foreach (var valueType in typeClassifier.ValueTypes)
            {
                if (customMethods.HasTypeSerializer(valueType))
                {
                    continue;                     // Don't generate serializers where custom methods exist
                }
                var serializeMethod = valueTypeSerializerType.CreateMethod(valueType, "ValueTypeSerialize",
                                                                           typeof(void), new[] { typeof(SerializeContext), typeof(BinaryWriter), valueType.MakeByRefType() });
                generatedValueTypeSerializeMethods.Add(valueType, serializeMethod);

                var deserializeMethod = valueTypeDeserializerType.CreateMethod(valueType, "ValueTypeDeserialize",
                                                                               typeof(void), new[] { typeof(DeserializeContext), typeof(BinaryReader), valueType.MakeByRefType() });
                generatedValueTypeDeserializeMethods.Add(valueType, deserializeMethod);
            }

            #endregion


            #region Declare Reference Type Serializer Methods

            // Note: These methods have no type check/dispatch - fields that could reference derived types must go through the field methods instead of these!

            var referenceTypeSerializerType   = methodCreatorCreator.Create("ReferenceTypeSerializer");
            var referenceTypeDeserializerType = methodCreatorCreator.Create("ReferenceTypeDeserializer");

            var generatedReferenceTypeSerializeMethods   = new Dictionary <Type, MethodInfo>();
            var generatedReferenceTypeDeserializeMethods = new Dictionary <Type, MethodInfo>();

            foreach (var referenceType in typeClassifier.ReferenceTypes)
            {
                Debug.Assert(!referenceType.IsArray);
                if (customMethods.HasTypeSerializer(referenceType))
                {
                    continue;                     // Don't generate serializers where custom methods exist
                }
                var serializeMethod = referenceTypeSerializerType.CreateMethod(referenceType, "ReferenceTypeSerialize",
                                                                               typeof(void), new[] { typeof(SerializeContext), typeof(BinaryWriter), referenceType });
                generatedReferenceTypeSerializeMethods.Add(referenceType, serializeMethod);

                var deserializeMethod = referenceTypeDeserializerType.CreateMethod(referenceType,
                                                                                   "ReferenceTypeDeserialize", typeof(void),
                                                                                   new[] { typeof(DeserializeContext), typeof(BinaryReader), referenceType });
                generatedReferenceTypeDeserializeMethods.Add(referenceType, deserializeMethod);
            }

            #endregion


            #region Declare Field Serializer Methods

            var fieldSerializerType   = methodCreatorCreator.Create("FieldSerializer");
            var fieldDeserializerType = methodCreatorCreator.Create("FieldDeserializer");

            var generatedReferenceFieldSerializeMethods   = new Dictionary <Type, MethodInfo>();
            var generatedReferenceFieldDeserializeMethods = new Dictionary <Type, MethodInfo>();

            foreach (var fieldType in typeClassifier.fieldSerializerDispatchLookup.Keys)
            {
                if (customMethods.HasFieldSerializer(fieldType))
                {
                    continue;                     // Don't create field serializers where custom ones exist
                }
                var serializeMethod = fieldSerializerType.CreateMethod(fieldType, "FieldSerialize", typeof(void),
                                                                       new[] { typeof(SerializeContext), typeof(BinaryWriter), fieldType });
                generatedReferenceFieldSerializeMethods.Add(fieldType, serializeMethod);

                var deserializeMethod = fieldDeserializerType.CreateMethod(fieldType, "FieldDeserialize", typeof(void),
                                                                           new[] { typeof(DeserializeContext), typeof(BinaryReader), fieldType.MakeByRefType() });
                generatedReferenceFieldDeserializeMethods.Add(fieldType, deserializeMethod);
            }

            #endregion


            #region Declare Dispatch Methods

            var serializerDispatchType   = methodCreatorCreator.Create("SerializerDispatch");
            var deserializerDispatchType = methodCreatorCreator.Create("DeserializerDispatch");

            // These are dispatched by ID#, so this is a lookup by that ID#
            var dynamicDispatchMethods = new List <SerializationMethods>(typeClassifier.dispatchableTypes.Count);

            // Dispatch to types that require actual dispatch (from type classification) as well as any suplemental types
            // (Suplemental types come from delegates, at the moment, a few of which require dynamic dispatch (see DelegateSerialization.SerializeDelegate)
            // and I haven't written a direct-dispatch path yet)
            HashSet <Type> dispatchTypes;
            if (suplementalDispatchTypes == null)
            {
                dispatchTypes = typeClassifier.dispatchableTypes;
            }
            else
            {
                dispatchTypes = new HashSet <Type>(typeClassifier.dispatchableTypes.Concat(suplementalDispatchTypes));
            }


            foreach (var type in dispatchTypes.NetworkOrder(t => t.Module.Name + t.FullName))
            {
                var idString = dynamicDispatchMethods.Count.ToString("0000");
                SerializationMethods sm;
                sm.type = type;

                sm.serializer = serializerDispatchType.CreateMethod(type, "SerializeWithId_" + idString, typeof(void),
                                                                    new[] { typeof(SerializeContext), typeof(BinaryWriter), typeof(object) });
                sm.deserializer = deserializerDispatchType.CreateMethod(type, "DeserializeFromId_" + idString, type,
                                                                        new[] { typeof(DeserializeContext), typeof(BinaryReader) });
                dynamicDispatchMethods.Add(sm);                 // Defines the ID#
            }

            // Deserialization dispatch method (this could reasonably use a table like serialization, but it was original a giant switch statement - so let's keep it that way)
            deserializationDispatcherMethod = deserializerDispatchType.CreateMethod(null, "DeserializationDispatcher",
                                                                                    typeof(object), new[] { typeof(DeserializeContext), typeof(BinaryReader) });

            #endregion


            #region Lookup Hookup

            // These are all used to do lookup during IL generation (should probably be switched to a SerializationMethodProviders)

            fieldSerializeMethods = FallbackMethodProvider.Combine(customMethods.ValueTypeSerializeMethods,
                                                                   customMethods.ReferenceFieldSerializeMethods,
                                                                   new LookupMethodProvider(generatedValueTypeSerializeMethods),
                                                                   new LookupMethodProvider(generatedReferenceFieldSerializeMethods));

            fieldDeserializeMethods = FallbackMethodProvider.Combine(
                customMethods.ValueTypeDeserializeMethods,
                customMethods.ReferenceFieldDeserializeMethods,
                new LookupMethodProvider(generatedValueTypeDeserializeMethods),
                new LookupMethodProvider(generatedReferenceFieldDeserializeMethods));

            referenceTypeSerializeMethods = FallbackMethodProvider.Combine(
                customMethods.ReferenceTypeSerializeMethods,
                new LookupMethodProvider(generatedReferenceTypeSerializeMethods));

            referenceTypeDeserializeMethods = FallbackMethodProvider.Combine(
                customMethods.ReferenceTypeDeserializeMethods,
                new LookupMethodProvider(generatedReferenceTypeDeserializeMethods));

            var serializeContext =
                new ILGenContext(Direction.Serialize, fieldSerializeMethods, referenceTypeSerializeMethods);
            var deserializeContext = new ILGenContext(Direction.Deserialize, fieldDeserializeMethods,
                                                      referenceTypeDeserializeMethods);

            #endregion


            #region Generate Dynamic Dispatch Method IL

            for (var i = 0; i < dynamicDispatchMethods.Count; i++)
            {
                // SerializeWithId Method:

                #region Generate IL

                {
                    var il = dynamicDispatchMethods[i].serializer.GetILGenerator();

                    // br.Write('i');
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Callvirt, Methods.BinaryWriter_WriteInt32);

                    // ReferenceTypeSerializer.Serialize(context, bw, (Class)obj);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldarg_2);
                    il.Emit(OpCodes.Castclass, dynamicDispatchMethods[i].type);
                    il.Emit(OpCodes.Call, referenceTypeSerializeMethods[dynamicDispatchMethods[i].type]);

                    il.Emit(OpCodes.Ret);
                }

                #endregion


                // DeserializeFromId Method:

                #region Generate IL

                {
                    var il = dynamicDispatchMethods[i].deserializer.GetILGenerator();
                    il.DeclareLocal(dynamicDispatchMethods[i].type);                     // Class obj

                    // Class obj = /* create object */
                    GenerateTypeCreation(il, dynamicDispatchMethods[i].type);
                    il.Emit(OpCodes.Stloc_0);

                    // ReferenceTypeDeserializer.Deserialize(context, br, (Class)obj);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Call, referenceTypeDeserializeMethods[dynamicDispatchMethods[i].type]);

                    // return obj;
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ret);
                }

                #endregion
            }

            #endregion


            #region Generate Reference Field Serializer Method IL

            foreach (var fieldDispatch in typeClassifier.fieldSerializerDispatchLookup)
            {
                var fieldType       = fieldDispatch.Key;
                var dispatchToTypes = fieldDispatch.Value;

                var serializeMethod   = generatedReferenceFieldSerializeMethods[fieldType];
                var deserializeMethod = generatedReferenceFieldDeserializeMethods[fieldType];

                GenerateReferenceFieldSerializer(serializeMethod.GetILGenerator(), fieldType, dispatchToTypes);
                GenerateReferenceFieldDeserializer(deserializeMethod.GetILGenerator(), fieldType, dispatchToTypes);
            }

            #endregion


            #region Generate Reference and Value Type Serializers

            foreach (var sm in generatedValueTypeSerializeMethods)
            {
                TypeSerializeILGeneration.GenerateValueTypeSerializationMethod(sm.Key, sm.Value.GetILGenerator(),
                                                                               serializeContext);
            }
            foreach (var sm in generatedValueTypeDeserializeMethods)
            {
                TypeSerializeILGeneration.GenerateValueTypeSerializationMethod(sm.Key, sm.Value.GetILGenerator(),
                                                                               deserializeContext);
            }
            foreach (var sm in generatedReferenceTypeSerializeMethods)
            {
                TypeSerializeILGeneration.GenerateReferenceTypeSerializationMethod(sm.Key, sm.Value.GetILGenerator(),
                                                                                   serializeContext);
            }
            foreach (var sm in generatedReferenceTypeDeserializeMethods)
            {
                TypeSerializeILGeneration.GenerateReferenceTypeSerializationMethod(sm.Key, sm.Value.GetILGenerator(),
                                                                                   deserializeContext);
            }

            #endregion


            #region Generate Deserialization Dispatcher (IL)

            {
                var il = deserializationDispatcherMethod.GetILGenerator();

                var dispatchLabels = new Label[dynamicDispatchMethods.Count];
                for (var i = 0; i < dispatchLabels.Length; i++)
                {
                    dispatchLabels[i] = il.DefineLabel();
                }
                var failLabel = il.DefineLabel();

                // br.ReadInt32()
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Callvirt,
                        typeof(BinaryReader)
                        .GetMethod("ReadInt32"));                         // NOTE: C# compiler loads this into two locals for no reason

                // switch(...)
                il.Emit(OpCodes.Switch, dispatchLabels);
                il.Emit(OpCodes.Br,
                        failLabel);                 // default case // NOTE: C# compiler emits br.s, but I bet it's not short with our huge table!

                for (var i = 0; i < dynamicDispatchMethods.Count; i++)
                {
                    // case 'i':
                    il.MarkLabel(dispatchLabels[i]);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Call, dynamicDispatchMethods[i].deserializer);
                    il.Emit(OpCodes.Ret);
                }

                // throw new Exception("...");
                il.MarkLabel(failLabel);
                il.Emit(OpCodes.Ldstr, "Unknown Type");
                il.Emit(OpCodes.Newobj, typeof(Exception).GetConstructor(new[] { typeof(string) }));
                il.Emit(OpCodes.Throw);
            }

            #endregion


            #region Fill Serialization Dispatch Table

            // We used to generate a table with IL in the serializer assembly (fancy). Now we can just do it directly.
            // (But we do have to do it last, so the methods are actually ready to convert to delegates)

            var dispatchTable = new Dictionary <Type, SerializeDispatchDelegate>();

            // Only dynamic methods can be converted to delegates (because the dynamic assembly we create in assembly-generating mode is save-only)
            if (methodCreatorCreator is DynamicMethodCreatorCreator)
            {
                for (var i = 0; i < dynamicDispatchMethods.Count; i++)
                {
                    var serializerMethod = dynamicDispatchMethods[i].serializer as DynamicMethod;
                    Debug.Assert(serializerMethod != null);
                    var serializerDelegate =
                        (SerializeDispatchDelegate)serializerMethod.CreateDelegate(typeof(SerializeDispatchDelegate));
                    dispatchTable.Add(dynamicDispatchMethods[i].type, serializerDelegate);
                }
            }

            #endregion


            #region Return

            var generatedMethodProviders = new SerializationMethodProviders(
                new LookupMethodProvider(generatedValueTypeSerializeMethods),
                new LookupMethodProvider(generatedValueTypeDeserializeMethods),
                new LookupMethodProvider(generatedReferenceTypeSerializeMethods),
                new LookupMethodProvider(generatedReferenceTypeDeserializeMethods),
                new LookupMethodProvider(generatedReferenceFieldSerializeMethods),
                new LookupMethodProvider(generatedReferenceFieldDeserializeMethods),
                new EmptyMethodProvider());


            // Note: custom methods come before generated methods:
            var combinedMethodProviders = SerializationMethodProviders
                                          .Combine(customMethods, generatedMethodProviders);


            DeserializeDispatchDelegate deserializeDispatchDelegate = null;
            if (methodCreatorCreator is DynamicMethodCreatorCreator
                )         // Only dynamic methods can be converted to delegates (our dynamic assembly is save-only)
            {
                deserializeDispatchDelegate =
                    (DeserializeDispatchDelegate)(deserializationDispatcherMethod as DynamicMethod).CreateDelegate(
                        typeof(DeserializeDispatchDelegate));
            }


            return(new GeneratorResult(combinedMethodProviders, dispatchTable, deserializeDispatchDelegate,
                                       null)); // module table gets filled later

            #endregion
        }
예제 #5
0
        public static GeneratorResult Run(Assembly[] assemblies, Type[] predefinedRoots, GeneratorReports reports,
                                          bool createAssembly, string outputAssemblyName)
        {
            var baseLocation =
                assemblies.First()
                .Location;                      // <- should probably use all passed assemblies as potential base locations (also detect accidently passing a system one)

            if (baseLocation == "")             // <- loaded from embedded resources
            {
                baseLocation = null;
            }
            else
            {
                baseLocation = Path.GetDirectoryName(baseLocation);
            }


            #region Initial Reporting - Assembly Info

            if (reports != null)
            {
                reports.Log.WriteLine(DateTime.Now);
                reports.Log.WriteLine();

                reports.Log.WriteLine("Initial Assemblies (" + assemblies.Count() + ")");
                foreach (var a in assemblies)
                {
                    reports.Log.WriteLine("  " + a);
                }
                reports.Log.WriteLine();

                reports.Log.WriteLine("Base Location = " + baseLocation ?? "<<resources>>");
                reports.Log.WriteLine();
            }

            #endregion


            #region Search for [SerializationRoot] attributes

            var rootTypes = new HashSet <Type>();

            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes();
                foreach (var type in types.Where(t => t.GetCustomAttributes(typeof(SerializationRootAttribute), false).Length > 0))
                {
                    rootTypes.Add(type);
                }
            }

            if (predefinedRoots != null)
            {
                foreach (var type in predefinedRoots)
                {
                    rootTypes.Add(type);
                }
            }

            if (reports != null)
            {
                reports.Log.WriteLine("Initial roots (" + rootTypes.Count + ")");
                foreach (var type in rootTypes)
                {
                    reports.Log.WriteLine("  " + type);
                }
                reports.Log.WriteLine();
            }

            #endregion


            #region Custom Method Discovery

            if (reports != null)
            {
                reports.Log.WriteLine("Running custom method discovery");
            }

            var customMethodAssemblies = new HashSet <Assembly>(assemblies);
            customMethodAssemblies.Add(typeof(SerializeList)
                                       .Assembly); // <- Get the assembly that contains all our "built-in" serializers

            var customMethods = CustomMethodDiscovery.Run(customMethodAssemblies,
                                                          reports != null ? reports.CustomMethodDiscovery : null, reports != null ? reports.Error : null);

            // Attach hard-coded array serializer methods:
            customMethods =
                SerializationMethodProviders.Combine(SerializeArray.CreateSerializationMethodProviders(),
                                                     customMethods);

            #endregion


            #region Type Discovery - First Pass

            var td = new TypeDiscovery(customMethods, assemblies);

            if (reports != null)
            {
                reports.Log.WriteLine("Running Type Discovery - First Pass");
                reports.TypeDiscovery.WriteLine("---------------------------");
                reports.TypeDiscovery.WriteLine("TYPE DISCOVERY - FIRST PASS");
                reports.TypeDiscovery.WriteLine("---------------------------");
                reports.TypeDiscovery.WriteLine();
            }

            td.DiscoverFromRoots(rootTypes,
                                 reports != null ? reports.TypeDiscovery : null, reports != null ? reports.Error : null);

            #endregion


            #region Delegate Discovery and Type Discovery Second Pass

            List <DelegateUsage> delegateDiscoveryResult = null;
            IEnumerable <Type>   allDelegateTargetTypes  = null;

            if (td.FoundDelegates)
            {
                Debug.WriteLine("IMPORTANT: Serializer generator is doing delegate discovery! May be undesireable.");

                #region Delegate Discovery

                if (reports != null)
                {
                    reports.Log.WriteLine("Running Delegate Discovery");
                }

                // Only search for delegates in our own assemblies:
                var delegateDiscoveryAssemblies = td.Assemblies.Where(a => a.IsOurAssembly(baseLocation));

                if (reports != null)
                {
                    reports.DelegateDiscovery.WriteLine("Searching in assemblies:");
                    foreach (var a in delegateDiscoveryAssemblies)
                    {
                        reports.DelegateDiscovery.WriteLine("  " + a);
                    }
                    reports.DelegateDiscovery.WriteLine();
                    reports.DelegateDiscovery.WriteLine();
                }

                delegateDiscoveryResult = DelegateDiscovery.Run(delegateDiscoveryAssemblies,
                                                                reports != null ? reports.DelegateDiscovery : null, reports != null ? reports.Error : null);

                if (reports != null)
                {
                    DelegateDiscovery.WriteDelegateUsageGrouped(delegateDiscoveryResult,
                                                                reports.DelegateDiscoveryGrouped);
                }

                #endregion


                #region Type Discovery - Second Pass

                if (reports != null)
                {
                    reports.Log.WriteLine("Running Type Discovery - Second Pass");
                    reports.TypeDiscovery.WriteLine("----------------------------");
                    reports.TypeDiscovery.WriteLine("TYPE DISCOVERY - SECOND PASS");
                    reports.TypeDiscovery.WriteLine("----------------------------");
                    reports.TypeDiscovery.WriteLine();
                }

                allDelegateTargetTypes = delegateDiscoveryResult.Select(du => du.targetType)
                                         .Where(t => t != null) // <- Disregard delegates with static targets
                                         .Distinct();

                td.DiscoverFromRoots(allDelegateTargetTypes,
                                     reports != null ? reports.TypeDiscovery : null, reports != null ? reports.Error : null);

                #endregion
            }
            else
            {
                if (reports != null)
                {
                    reports.Log.WriteLine("Was able to skip delegate discovery!");
                }
            }

            #endregion


            #region Type Classification

            if (reports != null)
            {
                reports.Log.WriteLine("Running type classification");
            }

            var tc = new TypeClassifier(td);
            tc.RunClassification();

            if (reports != null)
            {
                tc.WriteReport(reports.TypeClassification, reports.Error);

                CustomMethodDiscovery.CheckForDerivedCustomInitializers(customMethods.ReferenceTypeInitializeMethods,
                                                                        tc.ReferenceTypes, reports.Error);
            }

            #endregion


            #region Delegate Classification

            Dictionary <Type, DelegateTypeInfo> delegateTypeTable = null;

            if (delegateDiscoveryResult != null)
            {
                var dc = new DelegateClassification(td.delegateFieldTypes, delegateDiscoveryResult);

                delegateTypeTable = dc.GenerateDelegateTypeTable();

                if (reports != null)
                {
                    dc.Report(reports.DelegateClassification, reports.Error);

                    // NOTE: Any methods that appear in this list consitute a security risk, as they can be sent across the network!
                    //       Make sure that all these methods are "safe" (nothing that can, say, access the filesystem)
                    foreach (var methodName in delegateTypeTable.SelectMany(dt => dt.Value.methodInfoList)
                             .Select(mi => mi.method).Distinct()
                             .Select(m => m.DeclaringType + "." + m.Name).OrderBy(s => s))
                    {
                        reports.DelegateMethods.WriteLine(methodName);
                    }
                }

                // Attach delegate serializer method generator
                // NOTE: Doing this after type discovery, which automatically ignores delegates anyway, but before IL generation, which requires the methods to call
                // NOTE: Generated delegate serializers come *after* custom methods, because the user may specify custom serialization for any given delegate
                customMethods = SerializationMethodProviders.Combine(customMethods,
                                                                     DelegateSerialization.CreateSerializationMethodProviders());
            }

            #endregion


            #region Module Table

            var moduleTable = td.Assemblies.Where(a => a.IsOurAssembly(baseLocation)).SelectMany(a => a.GetModules())
                              .NetworkOrder(module => module.Name).ToList();

            #endregion


            #region Report Final Log Info

            if (reports != null)
            {
                reports.Log.WriteLine();
                reports.Log.WriteLine();

                var outsideTypes = td.valueTypes.Concat(td.referenceTypes)
                                   .Select(t =>
                                           (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable <>)
                                                        ? t.GetGenericArguments()[0]
                                                        : t))
                                   .Where(t => !customMethods.HasTypeSerializer(t))
                                   .Where(t => !t.Assembly.IsOurAssembly(baseLocation));

                reports.Log.WriteLine("Types outside of \"" + baseLocation + "\" without custom serializers (" +
                                      outsideTypes.Count() + ")");
                foreach (var type in outsideTypes)
                {
                    reports.Log.WriteLine("  " + type);
                }
                reports.Log.WriteLine();


                if (delegateDiscoveryResult != null)
                {
                    var openConstructedDelegates = delegateDiscoveryResult.Where(d =>
                                                                                 d.delegateType.ContainsGenericParameters || d.delegateMethod.ContainsGenericParameters);

                    reports.Log.WriteLine("Open constructed delegates (" + openConstructedDelegates.Count() + ")");
                    foreach (var d in openConstructedDelegates)
                    {
                        reports.Log.WriteLine("  " + d.delegateType + " -> " + d.delegateMethod + " -> " +
                                              (d.targetType != null ? d.targetType.ToString() : "(null)"));
                    }
                    reports.Log.WriteLine();
                }


                reports.Log.WriteLine();
                reports.Log.WriteLine("Assemblies (" + td.Assemblies.Count() + ")");
                foreach (var a in td.Assemblies)
                {
                    reports.Log.WriteLine("  " + a);
                }
                reports.Log.WriteLine();


                reports.Log.WriteLine();
                reports.Log.WriteLine("Module Table (" + moduleTable.Count() + ")");
                foreach (var module in moduleTable)
                {
                    reports.Log.WriteLine("  " + module);
                }
                reports.Log.WriteLine();


                var notInInitialAssemblies = td.Assemblies.Where(a => a.IsOurAssembly(baseLocation))
                                             .Where(a => !assemblies.Contains(a));
                if (notInInitialAssemblies.Count() > 0)
                {
                    reports.Error.WriteLine("WARNING: Visited assemblies in \"" + baseLocation +
                                            "\" that were not in initial list of assemblies (see log for suggested command line)");
                    reports.Error.WriteLine(
                        "  (It is possible that custom serialize methods, delegate instantiations, and derived types in these assemblies were missed)");
                    foreach (var a in notInInitialAssemblies)
                    {
                        reports.Error.WriteLine("  " + a);
                    }
                    reports.Error.WriteLine();
                }


                reports.Log.WriteLine();
                reports.Log.WriteLine("Suggested initial assemblies for command line:");
                foreach (var a in td.Assemblies.Where(a => a.IsOurAssembly(baseLocation)))
                {
                    reports.Log.Write("\"" + a.Location + "\" ");
                }
                reports.Log.WriteLine();
                reports.Log.WriteLine();


                // Final Counts:
                if (delegateDiscoveryResult != null)
                {
                    reports.Log.WriteLine();
                    reports.Log.WriteLine("Delegate Usage Count = " + delegateDiscoveryResult.Count);
                    reports.Log.WriteLine("Distinct Delegate Method Count = " +
                                          delegateDiscoveryResult.Select(du => du.delegateMethod).Distinct().Count());
                    reports.Log.WriteLine("Distinct Delegate Target Type Count = " +
                                          delegateDiscoveryResult.Select(du => du.targetType).Distinct().Count());
                }

                reports.Log.WriteLine();
                reports.Log.WriteLine("Total Serializable Type Count = " +
                                      (td.referenceTypes.Count + td.valueTypes.Count));
                reports.Log.WriteLine();

                reports.Log.WriteLine();
            }

            #endregion


            #region Generate Assembly

            var             serializerMethodGenerator = new SerializerMethodGenerator(tc, customMethods, allDelegateTargetTypes);
            GeneratorResult generatorResult;


            if (createAssembly)
            {
                if (reports != null)
                {
                    reports.Log.WriteLine("Generating assembly...");
                }

#if NET40 || NET45 || NET462
                var an = new AssemblyName(outputAssemblyName);
                var assemblyBuilder      = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
                var moduleBuilder        = assemblyBuilder.DefineDynamicModule(outputAssemblyName + ".dll");
                var methodCreatorCreator = new MethodBuilderCreatorCreator(moduleBuilder, "GenSerialize");
                generatorResult = serializerMethodGenerator.Generate(methodCreatorCreator);
                methodCreatorCreator.Finish();
                assemblyBuilder.Save(outputAssemblyName + ".dll");
#else
// https://github.com/dotnet/roslyn/issues/10881
// https://github.com/dotnet/corert/tree/master/src/ILVerify
                throw new NotSupportedException("Cannot convert dynamic assembly to disk bytes in Roslyn without parsing a syntax tree!");

                AssemblyName    an = new AssemblyName(outputAssemblyName);
                AssemblyBuilder assemblyBuilder =
                    AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndCollect);
                ModuleBuilder moduleBuilder        = assemblyBuilder.DefineDynamicModule(outputAssemblyName + ".dll");
                var           methodCreatorCreator = new MethodBuilderCreatorCreator(moduleBuilder, "GenSerialize");
                generatorResult = serializerMethodGenerator.Generate(methodCreatorCreator);
                methodCreatorCreator.Finish();

                Assembly assembly    = moduleBuilder.Assembly;
                var      compilation = CSharpCompilation.Create(outputAssemblyName);
#endif
            }
            else
            {
                if (reports != null)
                {
                    reports.Log.WriteLine("Generating dynamic methods...");
                }

                var methodCreatorCreator = new DynamicMethodCreatorCreator();
                generatorResult = serializerMethodGenerator.Generate(methodCreatorCreator);
            }

            if (reports != null)
            {
                reports.Log.WriteLine();
                reports.Log.WriteLine("Done!");
            }

            #endregion

            generatorResult.delegateTypeTable = delegateTypeTable;
            generatorResult.moduleTable       = moduleTable;
            return(generatorResult);
        }