Files
beyond/Assets/ThirdParty/FullSerializer/Editor/fsAotConfigurationEditor.cs
2024-11-20 15:21:28 +01:00

250 lines
7.6 KiB
C#

#if !NO_UNITY
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using FullSerializer.Internal;
using UnityEditor;
using UnityEngine;
namespace FullSerializer {
[InitializeOnLoad]
public static class PlayStateNotifier {
static PlayStateNotifier() {
EditorApplication.playmodeStateChanged += ModeChanged;
}
private static void ModeChanged () {
if (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) {
Debug.Log("There are " + fsAotCompilationManager.AotCandidateTypes.Count + " candidate types");
foreach (fsAotConfiguration target in Resources.FindObjectsOfTypeAll<fsAotConfiguration>()) {
var seen = new HashSet<string>(target.aotTypes.Select(t => t.FullTypeName));
foreach (Type type in fsAotCompilationManager.AotCandidateTypes) {
if (seen.Contains(type.FullName) == false) {
target.aotTypes.Add(new fsAotConfiguration.Entry(type));
EditorUtility.SetDirty(target);
}
}
}
}
}
}
[CustomEditor(typeof(fsAotConfiguration))]
public class fsAotConfigurationEditor : Editor {
[NonSerialized]
private List<Type> _allAotTypes;
private List<Type> allAotTypes {
get {
if (_allAotTypes == null)
_allAotTypes = FindAllAotTypes().ToList();
return _allAotTypes;
}
}
private string[] options = new string[] { "On", "Off", "[?]" };
private int GetIndexForState(fsAotConfiguration.AotState state) {
switch (state) {
case fsAotConfiguration.AotState.Enabled:
return 0;
case fsAotConfiguration.AotState.Disabled:
return 1;
case fsAotConfiguration.AotState.Default:
return 2;
}
throw new ArgumentException("state is invalid " + state);
}
private fsAotConfiguration.AotState GetStateForIndex(int index) {
switch (index) {
case 0: return fsAotConfiguration.AotState.Enabled;
case 1: return fsAotConfiguration.AotState.Disabled;
case 2: return fsAotConfiguration.AotState.Default;
}
throw new ArgumentException("invalid index " + index);
}
private IEnumerable<Type> FindAllAotTypes() {
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
foreach (Type t in assembly.GetTypes()) {
bool performAot = false;
// check for [fsObject]
{
var props = t.GetCustomAttributes(typeof(fsObjectAttribute), true);
if (props != null && props.Length > 0) performAot = true;
}
// check for [fsProperty]
if (!performAot) {
foreach (PropertyInfo p in t.GetProperties()) {
var props = p.GetCustomAttributes(typeof(fsPropertyAttribute), true);
if (props.Length > 0) {
performAot = true;
break;
}
}
}
if (performAot)
yield return t;
}
}
}
private enum OutOfDateResult {
NoAot,
Stale,
Current
}
private OutOfDateResult IsOutOfDate(Type type) {
string converterName = fsAotCompilationManager.GetQualifiedConverterNameForType(type);
Type converterType = fsTypeCache.GetType(converterName);
if (converterType == null)
return OutOfDateResult.NoAot;
// TODO: We should also verify that the type is contained inside of fsConverterRegistrar as
// an additional diagnostic. If it is not, then that means the type will not be used
// at runtime.
object instance_ = null;
try {
instance_ = Activator.CreateInstance(converterType);
} catch (Exception) {}
if (instance_ is fsIAotConverter == false)
return OutOfDateResult.NoAot;
var instance = (fsIAotConverter)instance_;
var metatype = fsMetaType.Get(new fsConfig(), type);
if (fsAotCompilationManager.IsAotModelUpToDate(metatype, instance) == false)
return OutOfDateResult.Stale;
return OutOfDateResult.Current;
}
private void DrawType(fsAotConfiguration.Entry entry, Type resolvedType) {
var target = (fsAotConfiguration)this.target;
EditorGUILayout.BeginHorizontal();
int currentIndex = GetIndexForState(entry.State);
int newIndex = GUILayout.Toolbar(currentIndex, options, GUILayout.ExpandWidth(false));
if (currentIndex != newIndex) {
entry.State = GetStateForIndex(newIndex);
target.UpdateOrAddEntry(entry);
EditorUtility.SetDirty(target);
}
string displayName = entry.FullTypeName;
string tooltip = "";
if (resolvedType != null) {
displayName = resolvedType.CSharpName();
tooltip = resolvedType.CSharpName(true);
}
GUIContent label = new GUIContent(displayName, tooltip);
GUILayout.Label(label);
GUILayout.FlexibleSpace();
GUIStyle messageStyle = new GUIStyle(EditorStyles.label);
string message;
if (resolvedType != null) {
message = GetAotCompilationMessage(resolvedType);
if (string.IsNullOrEmpty(message) == false) {
messageStyle.normal.textColor = Color.red;
} else {
switch (IsOutOfDate(resolvedType)) {
case OutOfDateResult.NoAot:
message = "No AOT model found";
break;
case OutOfDateResult.Stale:
message = "Stale";
break;
case OutOfDateResult.Current:
message = "\u2713";
break;
}
}
} else {
message = "Cannot load type";
}
GUILayout.Label(message, messageStyle);
EditorGUILayout.EndHorizontal();
}
private string GetAotCompilationMessage(Type type) {
try {
fsMetaType.Get(new fsConfig(), type).EmitAotData(true);
} catch (Exception e) {
return e.Message;
}
return "";
}
private Vector2 _scrollPos;
public override void OnInspectorGUI() {
var target = (fsAotConfiguration)this.target;
if (GUILayout.Button("Compile")) {
if (Directory.Exists(target.outputDirectory) == false)
Directory.CreateDirectory(target.outputDirectory);
foreach (fsAotConfiguration.Entry entry in target.aotTypes) {
if (entry.State == fsAotConfiguration.AotState.Enabled) {
Type resolvedType = fsTypeCache.GetType(entry.FullTypeName);
if (resolvedType == null) {
Debug.LogError("Cannot find type " + entry.FullTypeName);
continue;
}
try {
string compilation = fsAotCompilationManager.RunAotCompilationForType(new fsConfig(), resolvedType);
string path = Path.Combine(target.outputDirectory, "AotConverter_" + resolvedType.CSharpName(true, true) + ".cs");
File.WriteAllText(path, compilation);
} catch (Exception e) {
Debug.LogWarning("AOT compiling " + resolvedType.CSharpName(true) + " failed: " + e.Message);
}
}
}
AssetDatabase.Refresh();
}
target.outputDirectory = EditorGUILayout.TextField("Output Directory", target.outputDirectory);
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Set All");
int newIndex = GUILayout.Toolbar(-1, options, GUILayout.ExpandWidth(false));
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if (newIndex != -1) {
var newState = fsAotConfiguration.AotState.Default;
if (newIndex == 0)
newState = fsAotConfiguration.AotState.Enabled;
else if (newIndex == 1)
newState = fsAotConfiguration.AotState.Disabled;
for (int i = 0; i < target.aotTypes.Count; ++i) {
var entry = target.aotTypes[i];
entry.State = newState;
target.aotTypes[i] = entry;
}
}
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
foreach (fsAotConfiguration.Entry entry in target.aotTypes) {
Type resolvedType = fsTypeCache.GetType(entry.FullTypeName);
EditorGUI.BeginDisabledGroup(resolvedType == null || string.IsNullOrEmpty(GetAotCompilationMessage(resolvedType)) == false);
DrawType(entry, resolvedType);
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndScrollView();
}
}
}
#endif