File: ILCompiler\Metadata\EntityMap.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.MetadataTransform\ILCompiler.MetadataTransform.csproj (ILCompiler.MetadataTransform)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace ILCompiler.Metadata
{
    internal struct EntityMap<TEntity, TRecord>
    {
        private Dictionary<TEntity, TRecord> _map;

        public IReadOnlyCollection<TRecord> Records
        {
            get { return _map.Values; }
        }

        public IEnumerable<KeyValuePair<TEntity, TRecord>> Entries => _map;

        public EntityMap(IEqualityComparer<TEntity> comparer)
        {
            _map = new Dictionary<TEntity, TRecord>(comparer);
        }

        public TRecord GetOrCreate<TConcreteEntity, TConcreteRecord>(TConcreteEntity entity, Action<TConcreteEntity, TConcreteRecord> initializer)
            where TConcreteEntity : TEntity
            where TConcreteRecord : TRecord, new()
        {
            TRecord record;
            if (!_map.TryGetValue(entity, out record))
            {
                // We are externalizing the allocation instead of having a 'creator' delegate
                // because initializer might end up recursing into GetOrCreate for the same entity.
                // EntityMap needs to be ready to return a pointer to the currently initialized record.

                // The transform doesn't care that the record is not fully initialized yet
                // since we're not reading it at this stage.

                // Example:
                //
                // class FooAttribute : Attribute
                // {
                //     [FooAttribute]
                //     public FooAttribute()
                //     {
                //     }
                // }
                //
                // In here, while we're emitting the record for FooAttribute..ctor, we need
                // a pointer to the record for FooAttribute..ctor because that's what the
                // constructor of the custom attribute applied to the constructor.

                TConcreteRecord concreteRecord = new TConcreteRecord();
                _map.Add(entity, concreteRecord);

                initializer(entity, concreteRecord);

                return concreteRecord;
            }

            return record;
        }

        public bool TryGet(TEntity entity, out TRecord record)
        {
            return _map.TryGetValue(entity, out record);
        }

        public TConcreteRecord Create<TConcreteEntity, TConcreteRecord>(TConcreteEntity entity, Action<TConcreteEntity, TConcreteRecord> initializer)
            where TConcreteEntity : TEntity
            where TConcreteRecord : TRecord, new()
        {
            TConcreteRecord concreteRecord = new TConcreteRecord();

            // Important: add to the map before calling the initializer.
            // For reasoning see GetOrCreate.
            _map.Add(entity, concreteRecord);

            initializer(entity, concreteRecord);

            return concreteRecord;
        }
    }
}