@@ -43,28 +43,90 @@ public SemanticModel Model(SyntaxNode node)
4343 int NewId ( ) => TrapWriter . IdCounter ++ ;
4444
4545 /// <summary>
46- /// Gets the cached label for the given entity, or creates a new
47- /// (cached) label if it hasn't already been created. The label
48- /// is set on the supplied <paramref name="entity"/> object.
46+ /// Creates a new entity using the factory.
4947 /// </summary>
50- /// <returns><code>true</code> iff the label already existed.</returns>
51- public bool GetOrAddCachedLabel ( ICachedEntity entity )
48+ /// <param name="factory">The entity factory.</param>
49+ /// <param name="init">The initializer for the entity.</param>
50+ /// <returns>The new/existing entity.</returns>
51+ public Entity CreateEntity < Type , Entity > ( ICachedEntityFactory < Type , Entity > factory , Type init ) where Entity : ICachedEntity
5252 {
53- var id = GetId ( entity ) ;
54- if ( id == null )
55- throw new InternalError ( "Attempt to create a null entity for {0}" , entity . GetType ( ) ) ;
53+ return init == null ? CreateEntity2 ( factory , init ) : CreateNonNullEntity ( factory , init ) ;
54+ }
5655
57- Label existingLabel ;
58- if ( labelCache . TryGetValue ( id , out existingLabel ) )
56+ /// <summary>
57+ /// Creates a new entity using the factory.
58+ /// Uses a different cache to <see cref="CreateEntity{Type, Entity}(ICachedEntityFactory{Type, Entity}, Type)"/>,
59+ /// and can store null values.
60+ /// </summary>
61+ /// <param name="factory">The entity factory.</param>
62+ /// <param name="init">The initializer for the entity.</param>
63+ /// <returns>The new/existing entity.</returns>
64+ public Entity CreateEntity2 < Type , Entity > ( ICachedEntityFactory < Type , Entity > factory , Type init ) where Entity : ICachedEntity
65+ {
66+ using ( StackGuard )
5967 {
60- entity . Label = existingLabel ;
61- return true ;
68+ var entity = factory . Create ( this , init ) ;
69+
70+ if ( entityLabelCache . TryGetValue ( entity , out var label ) )
71+ {
72+ entity . Label = label ;
73+ }
74+ else
75+ {
76+ var id = entity . Id ;
77+ #if DEBUG_LABELS
78+ CheckEntityHasUniqueLabel ( id , entity ) ;
79+ #endif
80+ label = new Label ( NewId ( ) ) ;
81+ entity . Label = label ;
82+ entityLabelCache [ entity ] = label ;
83+ DefineLabel ( label , id ) ;
84+ if ( entity . NeedsPopulation )
85+ Populate ( init as ISymbol , entity ) ;
86+ }
87+ return entity ;
6288 }
89+ }
6390
64- entity . Label = new Label ( NewId ( ) ) ;
65- DefineLabel ( entity . Label , id ) ;
66- labelCache [ id ] = entity . Label ;
67- return false ;
91+ #if DEBUG_LABELS
92+ private void CheckEntityHasUniqueLabel ( IId id , ICachedEntity entity )
93+ {
94+ if ( idLabelCache . TryGetValue ( id , out var originalEntity ) )
95+ {
96+ Extractor . Message ( new Message { message = "Label collision for " + id . ToString ( ) , severity = Severity . Warning } ) ;
97+ }
98+ else
99+ {
100+ idLabelCache [ id ] = entity ;
101+ }
102+ }
103+ #endif
104+
105+ private Entity CreateNonNullEntity < Type , Entity > ( ICachedEntityFactory < Type , Entity > factory , Type init ) where Entity : ICachedEntity
106+ {
107+ if ( objectEntityCache . TryGetValue ( init , out var cached ) )
108+ return ( Entity ) cached ;
109+
110+ using ( StackGuard )
111+ {
112+ var label = new Label ( NewId ( ) ) ;
113+ var entity = factory . Create ( this , init ) ;
114+ entity . Label = label ;
115+
116+ objectEntityCache [ init ] = entity ;
117+
118+ var id = entity . Id ;
119+ DefineLabel ( label , id ) ;
120+
121+ #if DEBUG_LABELS
122+ CheckEntityHasUniqueLabel ( id , entity ) ;
123+ #endif
124+
125+ if ( entity . NeedsPopulation )
126+ Populate ( init as ISymbol , entity ) ;
127+
128+ return entity ;
129+ }
68130 }
69131
70132 /// <summary>
@@ -90,25 +152,6 @@ public bool ExtractGenerics(ICachedEntity entity)
90152 }
91153 }
92154
93- /// <summary>
94- /// Gets the ID belonging to cached entity <paramref name="entity"/>.
95- ///
96- /// The ID itself is also cached, but unlike the label cache (which is used
97- /// to prevent reextraction/infinite loops), this is a pure performance
98- /// optimization. Moreover, the label cache is injective, which the ID cache
99- /// need not be.
100- /// </summary>
101- IId GetId ( ICachedEntity entity )
102- {
103- IId id ;
104- if ( ! idCache . TryGetValue ( entity , out id ) )
105- {
106- id = entity . Id ;
107- idCache [ entity ] = id ;
108- }
109- return id ;
110- }
111-
112155 /// <summary>
113156 /// Creates a fresh label with ID "*", and set it on the
114157 /// supplied <paramref name="entity"/> object.
@@ -120,7 +163,11 @@ public void AddFreshLabel(IEntity entity)
120163 entity . Label = label ;
121164 }
122165
123- readonly Dictionary < IId , Label > labelCache = new Dictionary < IId , Label > ( ) ;
166+ #if DEBUG_LABELS
167+ readonly Dictionary < IId , ICachedEntity > idLabelCache = new Dictionary < IId , ICachedEntity > ( ) ;
168+ #endif
169+ readonly Dictionary < object , ICachedEntity > objectEntityCache = new Dictionary < object , ICachedEntity > ( ) ;
170+ readonly Dictionary < ICachedEntity , Label > entityLabelCache = new Dictionary < ICachedEntity , Label > ( ) ;
124171 readonly HashSet < Label > extractedGenerics = new HashSet < Label > ( ) ;
125172
126173 public void DefineLabel ( IEntity entity )
@@ -134,8 +181,6 @@ void DefineLabel(Label label, IId id)
134181 TrapWriter . Emit ( new DefineLabelEmitter ( label , id ) ) ;
135182 }
136183
137- readonly Dictionary < object , IId > idCache = new Dictionary < object , IId > ( ) ;
138-
139184 /// <summary>
140185 /// Queue of items to populate later.
141186 /// The only reason for this is so that the call stack does not
0 commit comments