Skip to content

Commit f43c7f9

Browse files
committed
Provide producation quality AOT implementation.
This changes how AOT works quite a bit: - AOT models are now versioned. An AOT model will only be used if it is up to date. - Unity editor integration to easily generate new AOT models.
1 parent 3ee7d00 commit f43c7f9

15 files changed

+561
-90
lines changed

Assets/FullSerializer/Source/Aot.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/FullSerializer/Source/Aot/Editor.meta

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#if !NO_UNITY
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
7+
using FullSerializer.Internal;
8+
using UnityEditor;
9+
using UnityEngine;
10+
11+
namespace FullSerializer {
12+
[InitializeOnLoad]
13+
public static class PlayStateNotifier {
14+
static PlayStateNotifier() {
15+
EditorApplication.playmodeStateChanged += ModeChanged;
16+
}
17+
18+
private static void ModeChanged () {
19+
if (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) {
20+
Debug.Log("There are " + fsAotCompilationManager.AotCandidateTypes.Count + " candidate types");
21+
foreach (fsAotConfiguration target in Resources.FindObjectsOfTypeAll<fsAotConfiguration>()) {
22+
var seen = new HashSet<string>(target.aotTypes.Select(t => t.FullTypeName));
23+
foreach (Type type in fsAotCompilationManager.AotCandidateTypes) {
24+
if (seen.Contains(type.FullName) == false) {
25+
target.aotTypes.Add(new fsAotConfiguration.Entry(type));
26+
EditorUtility.SetDirty(target);
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
34+
[CustomEditor(typeof(fsAotConfiguration))]
35+
public class fsAotConfigurationEditor : Editor {
36+
[NonSerialized]
37+
private List<Type> _allAotTypes;
38+
private List<Type> allAotTypes {
39+
get {
40+
if (_allAotTypes == null)
41+
_allAotTypes = FindAllAotTypes().ToList();
42+
return _allAotTypes;
43+
}
44+
}
45+
46+
private string[] options = new string[] { "On", "Off", "[?]" };
47+
private int GetIndexForState(fsAotConfiguration.AotState state) {
48+
switch (state) {
49+
case fsAotConfiguration.AotState.Enabled:
50+
return 0;
51+
case fsAotConfiguration.AotState.Disabled:
52+
return 1;
53+
case fsAotConfiguration.AotState.Default:
54+
return 2;
55+
}
56+
57+
throw new ArgumentException("state is invalid " + state);
58+
}
59+
private fsAotConfiguration.AotState GetStateForIndex(int index) {
60+
switch (index) {
61+
case 0: return fsAotConfiguration.AotState.Enabled;
62+
case 1: return fsAotConfiguration.AotState.Disabled;
63+
case 2: return fsAotConfiguration.AotState.Default;
64+
}
65+
66+
throw new ArgumentException("invalid index " + index);
67+
}
68+
69+
private IEnumerable<Type> FindAllAotTypes() {
70+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
71+
foreach (Type t in assembly.GetTypes()) {
72+
bool performAot = false;
73+
74+
// check for [fsObject]
75+
{
76+
var props = t.GetCustomAttributes(typeof(fsObjectAttribute), true);
77+
if (props != null && props.Length > 0) performAot = true;
78+
}
79+
80+
// check for [fsProperty]
81+
if (!performAot) {
82+
foreach (PropertyInfo p in t.GetProperties()) {
83+
var props = p.GetCustomAttributes(typeof(fsPropertyAttribute), true);
84+
if (props.Length > 0) {
85+
performAot = true;
86+
break;
87+
}
88+
}
89+
}
90+
91+
if (performAot)
92+
yield return t;
93+
}
94+
}
95+
}
96+
97+
private enum OutOfDateResult {
98+
NoAot,
99+
Stale,
100+
Current
101+
}
102+
private OutOfDateResult IsOutOfDate(Type type) {
103+
string converterName = fsAotCompilationManager.GetQualifiedConverterNameForType(type);
104+
Type converterType = fsTypeCache.GetType(converterName);
105+
if (converterType == null)
106+
return OutOfDateResult.NoAot;
107+
108+
// TODO: We should also verify that the type is contained inside of fsConverterRegistrar as
109+
// an additional diagnostic. If it is not, then that means the type will not be used
110+
// at runtime.
111+
112+
object instance_ = null;
113+
try {
114+
instance_ = Activator.CreateInstance(converterType);
115+
} catch (Exception) {}
116+
if (instance_ is fsIAotConverter == false)
117+
return OutOfDateResult.NoAot;
118+
var instance = (fsIAotConverter)instance_;
119+
120+
var metatype = fsMetaType.Get(new fsConfig(), type);
121+
if (fsAotCompilationManager.IsAotModelUpToDate(metatype, instance) == false)
122+
return OutOfDateResult.Stale;
123+
124+
return OutOfDateResult.Current;
125+
}
126+
127+
private void DrawType(fsAotConfiguration.Entry entry, Type resolvedType) {
128+
var target = (fsAotConfiguration)this.target;
129+
130+
EditorGUILayout.BeginHorizontal();
131+
132+
int currentIndex = GetIndexForState(entry.State);
133+
int newIndex = GUILayout.Toolbar(currentIndex, options, GUILayout.ExpandWidth(false));
134+
if (currentIndex != newIndex) {
135+
entry.State = GetStateForIndex(newIndex);
136+
target.UpdateOrAddEntry(entry);
137+
EditorUtility.SetDirty(target);
138+
}
139+
140+
string displayName = entry.FullTypeName;
141+
string tooltip = "";
142+
if (resolvedType != null) {
143+
displayName = resolvedType.CSharpName();
144+
tooltip = resolvedType.CSharpName(true);
145+
}
146+
GUIContent label = new GUIContent(displayName, tooltip);
147+
GUILayout.Label(label);
148+
149+
GUILayout.FlexibleSpace();
150+
151+
GUIStyle messageStyle = new GUIStyle(EditorStyles.label);
152+
string message;
153+
if (resolvedType != null) {
154+
message = GetAotCompilationMessage(resolvedType);
155+
if (string.IsNullOrEmpty(message) == false) {
156+
messageStyle.normal.textColor = Color.red;
157+
} else {
158+
switch (IsOutOfDate(resolvedType)) {
159+
case OutOfDateResult.NoAot:
160+
message = "No AOT model found";
161+
break;
162+
case OutOfDateResult.Stale:
163+
message = "Stale";
164+
break;
165+
case OutOfDateResult.Current:
166+
message = "\u2713";
167+
break;
168+
}
169+
}
170+
} else {
171+
message = "Cannot load type";
172+
}
173+
174+
GUILayout.Label(message, messageStyle);
175+
176+
EditorGUILayout.EndHorizontal();
177+
}
178+
179+
private string GetAotCompilationMessage(Type type) {
180+
try {
181+
fsMetaType.Get(new fsConfig(), type).EmitAotData(true);
182+
} catch (Exception e) {
183+
return e.Message;
184+
}
185+
return "";
186+
}
187+
188+
private Vector2 _scrollPos;
189+
public override void OnInspectorGUI() {
190+
var target = (fsAotConfiguration)this.target;
191+
192+
if (GUILayout.Button("Compile")) {
193+
if (Directory.Exists(target.outputDirectory) == false)
194+
Directory.CreateDirectory(target.outputDirectory);
195+
196+
foreach (fsAotConfiguration.Entry entry in target.aotTypes) {
197+
if (entry.State == fsAotConfiguration.AotState.Enabled) {
198+
Type resolvedType = fsTypeCache.GetType(entry.FullTypeName);
199+
if (resolvedType == null) {
200+
Debug.LogError("Cannot find type " + entry.FullTypeName);
201+
continue;
202+
}
203+
204+
try {
205+
string compilation = fsAotCompilationManager.RunAotCompilationForType(new fsConfig(), resolvedType);
206+
string path = Path.Combine(target.outputDirectory, "AotConverter_" + resolvedType.CSharpName(true, true) + ".cs");
207+
File.WriteAllText(path, compilation);
208+
} catch (Exception e) {
209+
Debug.LogWarning("AOT compiling " + resolvedType.CSharpName(true) + " failed: " + e.Message);
210+
}
211+
}
212+
}
213+
AssetDatabase.Refresh();
214+
}
215+
216+
target.outputDirectory = EditorGUILayout.TextField("Output Directory", target.outputDirectory);
217+
218+
EditorGUILayout.BeginHorizontal();
219+
GUILayout.Label("Set All");
220+
int newIndex = GUILayout.Toolbar(-1, options, GUILayout.ExpandWidth(false));
221+
GUILayout.FlexibleSpace();
222+
EditorGUILayout.EndHorizontal();
223+
if (newIndex != -1) {
224+
var newState = fsAotConfiguration.AotState.Default;
225+
if (newIndex == 0)
226+
newState = fsAotConfiguration.AotState.Enabled;
227+
else if (newIndex == 1)
228+
newState = fsAotConfiguration.AotState.Disabled;
229+
230+
for (int i = 0; i < target.aotTypes.Count; ++i) {
231+
var entry = target.aotTypes[i];
232+
entry.State = newState;
233+
target.aotTypes[i] = entry;
234+
}
235+
}
236+
237+
238+
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
239+
foreach (fsAotConfiguration.Entry entry in target.aotTypes) {
240+
Type resolvedType = fsTypeCache.GetType(entry.FullTypeName);
241+
EditorGUI.BeginDisabledGroup(resolvedType == null || string.IsNullOrEmpty(GetAotCompilationMessage(resolvedType)) == false);
242+
DrawType(entry, resolvedType);
243+
EditorGUI.EndDisabledGroup();
244+
}
245+
EditorGUILayout.EndScrollView();
246+
}
247+
248+
}
249+
}
250+
#endif

Assets/FullSerializer/Source/Aot/Editor/fsAotConfigurationEditor.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)