/// <summary> /// Generates an AOT DLL, using the given parameters. /// </summary> public static void GenerateDLL(string dirPath, string assemblyName, List <Type> supportSerializedTypes, bool generateLinkXml = true) { if (!dirPath.EndsWith("/")) { dirPath += "/"; } var newDllPath = dirPath + assemblyName; var fullDllPath = newDllPath + ".dll"; var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName() { Name = assemblyName }, AssemblyBuilderAccess.Save, dirPath); var module = assembly.DefineDynamicModule(assemblyName); assembly.SetCustomAttribute(new CustomAttributeBuilder(typeof(EmittedAssemblyAttribute).GetConstructor(new Type[0]), new object[0])); // The following is a fix for Unity's crappy Mono runtime that doesn't know how to do this sort // of stuff properly // // We must manually remove the "Default Dynamic Assembly" module that is automatically defined, // otherwise a reference to that non-existent module will be saved into the assembly's IL, and // that will cause a multitude of issues. // // We do this by forcing there to be only one module - the one we just defined, and we set the // manifest module to be that module as well. { var modulesField = assembly.GetType().GetField("modules", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var manifestModuleField = assembly.GetType().GetField("manifest_module", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (modulesField != null) { modulesField.SetValue(assembly, new ModuleBuilder[] { module }); } if (manifestModuleField != null) { manifestModuleField.SetValue(assembly, module); } } var type = module.DefineType(assemblyName + ".PreventCodeStrippingViaReferences", TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.NotPublic); CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(PreserveAttribute).GetConstructor(Type.EmptyTypes), new object[0]); type.SetCustomAttribute(attributeBuilder); var staticConstructor = type.DefineTypeInitializer(); var il = staticConstructor.GetILGenerator(); HashSet <Type> seenTypes = new HashSet <Type>(); //var endPoint = il.DefineLabel(); //il.Emit(OpCodes.Br, endPoint); foreach (var serializedType in supportSerializedTypes) { if (serializedType == null) { continue; } if (serializedType.IsAbstract || serializedType.IsInterface) { Debug.LogError("Skipping type '" + serializedType.GetNiceFullName() + "'! Type is abstract or an interface."); continue; } if (serializedType.IsGenericType && (serializedType.IsGenericTypeDefinition || !serializedType.IsFullyConstructedGenericType())) { Debug.LogError("Skipping type '" + serializedType.GetNiceFullName() + "'! Type is a generic type definition, or its arguments contain generic parameters; type must be a fully constructed generic type."); continue; } if (seenTypes.Contains(serializedType)) { continue; } seenTypes.Add(serializedType); // Reference serialized type { if (serializedType.IsValueType) { var local = il.DeclareLocal(serializedType); il.Emit(OpCodes.Ldloca, local); il.Emit(OpCodes.Initobj, serializedType); } else { var constructor = serializedType.GetConstructor(Type.EmptyTypes); if (constructor != null) { il.Emit(OpCodes.Newobj, constructor); il.Emit(OpCodes.Pop); } } } // Reference and/or create formatter type if (!FormatterUtilities.IsPrimitiveType(serializedType) && !typeof(UnityEngine.Object).IsAssignableFrom(serializedType)) { var actualFormatter = FormatterLocator.GetFormatter(serializedType, SerializationPolicies.Unity); if (actualFormatter.GetType().IsDefined <EmittedFormatterAttribute>()) { //TODO: Make emitted formatter code compatible with IL2CPP //// Emit an actual AOT formatter into the generated assembly //if (this.emitAOTFormatters) //{ // var emittedFormatter = FormatterEmitter.EmitAOTFormatter(typeEntry.Type, module, SerializationPolicies.Unity); // var emittedFormatterConstructor = emittedFormatter.GetConstructor(Type.EmptyTypes); // il.Emit(OpCodes.Newobj, emittedFormatterConstructor); // il.Emit(OpCodes.Pop); //} } var formatters = FormatterLocator.GetAllCompatiblePredefinedFormatters(serializedType, SerializationPolicies.Unity); foreach (var formatter in formatters) { // Reference the pre-existing formatter var formatterConstructor = formatter.GetType().GetConstructor(Type.EmptyTypes); if (formatterConstructor != null) { il.Emit(OpCodes.Newobj, formatterConstructor); il.Emit(OpCodes.Pop); } } //// Make sure we have a proper reflection formatter variant if all else goes wrong //il.Emit(OpCodes.Newobj, typeof(ReflectionFormatter<>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes)); //il.Emit(OpCodes.Pop); } ConstructorInfo serializerConstructor; // Reference serializer variant if (serializedType.IsEnum) { serializerConstructor = typeof(EnumSerializer <>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes); } else { serializerConstructor = typeof(ComplexTypeSerializer <>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes); } il.Emit(OpCodes.Newobj, serializerConstructor); il.Emit(OpCodes.Pop); } //il.MarkLabel(endPoint); il.Emit(OpCodes.Ret); type.CreateType(); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } if (File.Exists(fullDllPath)) { File.Delete(fullDllPath); } if (File.Exists(fullDllPath + ".meta")) { File.Delete(fullDllPath + ".meta"); } try { AssetDatabase.Refresh(); } catch (Exception) { // Sigh, Unity 5.3.0 } assembly.Save(assemblyName); File.Move(newDllPath, fullDllPath); if (generateLinkXml) { File.WriteAllText(dirPath + "link.xml", @"<linker> <assembly fullname=""" + assemblyName + @""" preserve=""all""/> </linker>"); } try { AssetDatabase.Refresh(); } catch (Exception) { // Sigh, Unity 5.3.0 } var pluginImporter = PluginImporter.GetAtPath(fullDllPath) as PluginImporter; if (pluginImporter != null) { //pluginImporter.ClearSettings(); pluginImporter.SetCompatibleWithEditor(false); pluginImporter.SetCompatibleWithAnyPlatform(true); // Disable for all standalones pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneLinux, false); pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneLinux64, false); pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneLinuxUniversal, false); // StandaloneOSXUniversal (<= 2017.2) / StandaloneOSX (>= 2017.3) pluginImporter.SetCompatibleWithPlatform((BuildTarget)2, false); if (!UnityVersion.IsVersionOrGreater(2017, 3)) { pluginImporter.SetCompatibleWithPlatform((BuildTarget)4, false); // StandaloneOSXIntel pluginImporter.SetCompatibleWithPlatform((BuildTarget)27, false); // StandaloneOSXIntel64 } pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false); pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false); //pluginImporter.SetCompatibleWithPlatform(BuildTarget.Android, false); pluginImporter.SaveAndReimport(); } AssetDatabase.SaveAssets(); }
/// <summary> /// Generates an AOT DLL, using the given parameters. /// </summary> public static void GenerateDLL(string dirPath, string assemblyName, List <Type> supportSerializedTypes, bool generateLinkXml = true) { #if UNITY_EDITOR && NET_4_6 if (!dirPath.EndsWith("/")) { dirPath += "/"; } var newDllPath = dirPath + assemblyName; var fullDllPath = newDllPath + ".dll"; var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName() { Name = assemblyName }, AssemblyBuilderAccess.Save, dirPath); var module = assembly.DefineDynamicModule(assemblyName); assembly.SetCustomAttribute(new CustomAttributeBuilder(typeof(EmittedAssemblyAttribute).GetConstructor(new Type[0]), new object[0])); // VRChat Edit: Add the UnityAPICompatibilityVersion Attribute for the current version of Unity to skip API Updating. #if UNITY_2019 assembly.SetCustomAttribute(new CustomAttributeBuilder( typeof(UnityAPICompatibilityVersionAttribute).GetConstructor(new[] { typeof(string), typeof(bool) }), new object[] { Application.unityVersion, true }) ); #else assembly.SetCustomAttribute(new CustomAttributeBuilder( typeof(UnityAPICompatibilityVersionAttribute).GetConstructor(new[] { typeof(string) }), new object[] { Application.unityVersion }) ); #endif // The following is a fix for Unity's crappy Mono runtime that doesn't know how to do this sort // of stuff properly // // We must manually remove the "Default Dynamic Assembly" module that is automatically defined, // otherwise a reference to that non-existent module will be saved into the assembly's IL, and // that will cause a multitude of issues. // // We do this by forcing there to be only one module - the one we just defined, and we set the // manifest module to be that module as well. { var modulesField = assembly.GetType().GetField("modules", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var manifestModuleField = assembly.GetType().GetField("manifest_module", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (modulesField != null) { modulesField.SetValue(assembly, new ModuleBuilder[] { module }); } if (manifestModuleField != null) { manifestModuleField.SetValue(assembly, module); } } var type = module.DefineType(assemblyName + ".PreventCodeStrippingViaReferences", TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.NotPublic); CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(PreserveAttribute).GetConstructor(Type.EmptyTypes), new object[0]); type.SetCustomAttribute(attributeBuilder); var staticConstructor = type.DefineTypeInitializer(); var il = staticConstructor.GetILGenerator(); var falseLocal = il.DeclareLocal(typeof(bool)); il.Emit(OpCodes.Ldc_I4_0); // Load false il.Emit(OpCodes.Stloc, falseLocal); // Set to falseLocal HashSet <Type> seenTypes = new HashSet <Type>(); if (UnityVersion.Major == 2019 && UnityVersion.Minor == 2) { // This here is a hack that fixes Unity's assembly updater triggering faultily in Unity 2019.2 // (and in early 2019.3 alphas/betas, but we're not handling those). When it triggers, it edits // the generated AOT assembly such that it immediately causes Unity to hard crash. Having this // type reference present in the assembly prevents that from happening. (Any concrete type in // the serialization assembly would work, this one is just a random pick.) // // Unity should have fixed this in 2019.3, but said that a backport to 2019.2 is not guaranteed // to occur, though it might. supportSerializedTypes.Add(typeof(DateTimeFormatter)); } //var endPoint = il.DefineLabel(); //il.Emit(OpCodes.Br, endPoint); foreach (var serializedType in supportSerializedTypes) { if (serializedType == null) { continue; } bool isAbstract = serializedType.IsAbstract || serializedType.IsInterface; if (serializedType.IsGenericType && (serializedType.IsGenericTypeDefinition || !serializedType.IsFullyConstructedGenericType())) { Debug.LogError("Skipping type '" + serializedType.GetNiceFullName() + "'! Type is a generic type definition, or its arguments contain generic parameters; type must be a fully constructed generic type."); continue; } if (seenTypes.Contains(serializedType)) { continue; } seenTypes.Add(serializedType); // Reference serialized type { if (serializedType.IsValueType) { var local = il.DeclareLocal(serializedType); il.Emit(OpCodes.Ldloca, local); il.Emit(OpCodes.Initobj, serializedType); } else if (!isAbstract) { var constructor = serializedType.GetConstructor(Type.EmptyTypes); if (constructor != null) { il.Emit(OpCodes.Newobj, constructor); il.Emit(OpCodes.Pop); } } } // Reference and/or create formatter type if (!FormatterUtilities.IsPrimitiveType(serializedType) && !typeof(UnityEngine.Object).IsAssignableFrom(serializedType) && !isAbstract) { var actualFormatter = FormatterLocator.GetFormatter(serializedType, SerializationPolicies.Unity); if (actualFormatter.GetType().IsDefined <EmittedFormatterAttribute>()) { //TODO: Make emitted formatter code compatible with IL2CPP //// Emit an actual AOT formatter into the generated assembly //if (this.emitAOTFormatters) //{ // var emittedFormatter = FormatterEmitter.EmitAOTFormatter(typeEntry.Type, module, SerializationPolicies.Unity); // var emittedFormatterConstructor = emittedFormatter.GetConstructor(Type.EmptyTypes); // il.Emit(OpCodes.Newobj, emittedFormatterConstructor); // il.Emit(OpCodes.Pop); //} } var formatters = FormatterLocator.GetAllCompatiblePredefinedFormatters(serializedType, SerializationPolicies.Unity); foreach (var formatter in formatters) { // Reference the pre-existing formatter var formatterConstructor = formatter.GetType().GetConstructor(Type.EmptyTypes); if (formatterConstructor != null) { il.Emit(OpCodes.Newobj, formatterConstructor); il.Emit(OpCodes.Pop); } } //// Make sure we have a proper reflection formatter variant if all else goes wrong //il.Emit(OpCodes.Newobj, typeof(ReflectionFormatter<>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes)); //il.Emit(OpCodes.Pop); } ConstructorInfo serializerConstructor; // Reference serializer variant if (serializedType.IsValueType) { serializerConstructor = Serializer.Get(serializedType).GetType().GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, serializerConstructor); // The following section is a fix for an issue on IL2CPP for PS4, where sometimes bytecode isn't // generated for methods in base types of needed types - FX, Serializer<T>.ReadValueWeak() // may be missing. This only seems to happen in a relevant way for value types. { var endLabel = il.DefineLabel(); // Load a false local value, then jump to the end of this segment of code due to that // false value. This is an attempt to trick any potential code flow analysis made // by IL2CPP that checks whether this segment of code is actually run. // // We don't run the code because if we did, that would actually throw a bunch of // exceptions from invalid calls to ReadValueWeak and WriteValueWeak. il.Emit(OpCodes.Ldloc, falseLocal); il.Emit(OpCodes.Brfalse, endLabel); var baseSerializerType = typeof(Serializer <>).MakeGenericType(serializedType); var readValueWeakMethod = baseSerializerType.GetMethod("ReadValueWeak", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, new Type[] { typeof(IDataReader) }, null); var writeValueWeakMethod = baseSerializerType.GetMethod("WriteValueWeak", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, new Type[] { typeof(string), typeof(object), typeof(IDataWriter) }, null); il.Emit(OpCodes.Dup); // Duplicate serializer instance il.Emit(OpCodes.Ldnull); // Load null argument for IDataReader reader il.Emit(OpCodes.Callvirt, readValueWeakMethod); // Call ReadValueWeak on serializer instance il.Emit(OpCodes.Pop); // Pop result of ReadValueWeak il.Emit(OpCodes.Dup); // Duplicate serializer instance il.Emit(OpCodes.Ldnull); // Load null argument for string name il.Emit(OpCodes.Ldnull); // Load null argument for object value il.Emit(OpCodes.Ldnull); // Load null argument for IDataWriter writer il.Emit(OpCodes.Callvirt, writeValueWeakMethod); // Call WriteValueWeak on serializer instance il.MarkLabel(endLabel); // This is where the code always jumps to, skipping the above } il.Emit(OpCodes.Pop); // Pop the serializer instance } else { serializerConstructor = typeof(ComplexTypeSerializer <>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, serializerConstructor); il.Emit(OpCodes.Pop); } } //il.MarkLabel(endPoint); il.Emit(OpCodes.Ret); type.CreateType(); if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } if (File.Exists(fullDllPath)) { File.Delete(fullDllPath); } if (File.Exists(fullDllPath + ".meta")) { File.Delete(fullDllPath + ".meta"); } try { AssetDatabase.Refresh(); } catch (Exception) { // Sigh, Unity 5.3.0 } assembly.Save(assemblyName); File.Move(newDllPath, fullDllPath); if (generateLinkXml) { File.WriteAllText(dirPath + "link.xml", @"<linker> <assembly fullname=""" + assemblyName + @""" preserve=""all""/> </linker>"); } try { AssetDatabase.Refresh(); } catch (Exception) { // Sigh, Unity 5.3.0 } var pluginImporter = PluginImporter.GetAtPath(fullDllPath) as PluginImporter; if (pluginImporter != null) { //pluginImporter.ClearSettings(); pluginImporter.SetCompatibleWithEditor(false); pluginImporter.SetCompatibleWithAnyPlatform(true); // Disable for all standalones pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneLinux64, false); if (!UnityVersion.IsVersionOrGreater(2019, 2)) { pluginImporter.SetCompatibleWithPlatform((BuildTarget)17, false); // StandaloneLinux pluginImporter.SetCompatibleWithPlatform((BuildTarget)25, false); // StandaloneLinuxUniversal } // StandaloneOSXUniversal (<= 2017.2) / StandaloneOSX (>= 2017.3) pluginImporter.SetCompatibleWithPlatform((BuildTarget)2, false); if (!UnityVersion.IsVersionOrGreater(2017, 3)) { pluginImporter.SetCompatibleWithPlatform((BuildTarget)4, false); // StandaloneOSXIntel pluginImporter.SetCompatibleWithPlatform((BuildTarget)27, false); // StandaloneOSXIntel64 } pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false); //pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false); //pluginImporter.SetCompatibleWithPlatform(BuildTarget.Android, false); pluginImporter.SaveAndReimport(); } AssetDatabase.SaveAssets(); #endif }