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
0 commit comments