diff --git a/src/Draco.Compiler/Api/Compilation.cs b/src/Draco.Compiler/Api/Compilation.cs index ed086906f..de35c071f 100644 --- a/src/Draco.Compiler/Api/Compilation.cs +++ b/src/Draco.Compiler/Api/Compilation.cs @@ -133,6 +133,11 @@ public static Compilation Create( /// internal WellKnownTypes WellKnownTypes { get; } + /// + /// The type provider used for metadata references. + /// + internal TypeProvider TypeProvider { get; } + /// /// Intrinsicly defined symbols for the compilation. /// @@ -153,6 +158,7 @@ private Compilation( ModuleSymbol? sourceModule = null, DeclarationTable? declarationTable = null, WellKnownTypes? wellKnownTypes = null, + TypeProvider? typeProvider = null, IntrinsicSymbols? intrinsicSymbols = null, BinderCache? binderCache = null) { @@ -166,6 +172,7 @@ private Compilation( this.sourceModule = sourceModule; this.declarationTable = declarationTable; this.WellKnownTypes = wellKnownTypes ?? new WellKnownTypes(this); + this.TypeProvider = typeProvider ?? new TypeProvider(this); this.IntrinsicSymbols = intrinsicSymbols ?? new IntrinsicSymbols(this); this.binderCache = binderCache ?? new BinderCache(this); } @@ -215,6 +222,10 @@ public Compilation UpdateSyntaxTree(SyntaxTree? oldTree, SyntaxTree? newTree) // Or we keep it as long as metadata refs don't change? // Just a cache wellKnownTypes: this.WellKnownTypes, + // TODO: We might want to change the compilation of type provider? + // Or we keep it as long as metadata refs don't change? + // Just a cache + typeProvider: this.TypeProvider, // TODO: We might want to change the compilation of intrinsic-symbols? // Or we keep it as long as metadata refs don't change? // Just a cache diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataFieldSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataFieldSymbol.cs index 41e404483..7e03e1d05 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataFieldSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataFieldSymbol.cs @@ -77,12 +77,8 @@ public MetadataFieldSymbol(Symbol containingSymbol, FieldDefinition fieldDefinit this.fieldDefinition = fieldDefinition; } - private TypeSymbol BuildType() - { - // Decode signature - var decoder = new TypeProvider(this.Assembly.Compilation); - return this.fieldDefinition.DecodeSignature(decoder, this); - } + private TypeSymbol BuildType() => + this.fieldDefinition.DecodeSignature(this.Assembly.Compilation.TypeProvider, this); private object? BuildDefaultValue() { diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs index 0cdf860e9..ee6f6106a 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataMethodSymbol.cs @@ -139,8 +139,7 @@ private ImmutableArray BuildGenericParameters() private void BuildSignature() { // Decode signature - var decoder = new TypeProvider(this.Assembly.Compilation); - var signature = this.methodDefinition.DecodeSignature(decoder, this); + var signature = this.methodDefinition.DecodeSignature(this.Assembly.Compilation.TypeProvider, this); // Build parameters var parameters = ImmutableArray.CreateBuilder(); @@ -194,7 +193,7 @@ private void BuildOverride() { var definition = this.MetadataReader.GetMethodDefinition(methodDef); var name = this.MetadataReader.GetString(definition.Name); - var provider = new TypeProvider(this.Assembly.Compilation); + var provider = this.Assembly.Compilation.TypeProvider; var signature = definition.DecodeSignature(provider, this); var containingType = provider.GetTypeFromDefinition(this.MetadataReader, definition.GetDeclaringType(), 0); return GetFunctionWithSignature(containingType, name, signature); @@ -204,7 +203,7 @@ private void BuildOverride() { var reference = this.MetadataReader.GetMemberReference(methodRef); var name = this.MetadataReader.GetString(reference.Name); - var provider = new TypeProvider(this.Assembly.Compilation); + var provider = this.Assembly.Compilation.TypeProvider; var signature = reference.DecodeMethodSignature(provider, this); var containingType = provider.GetTypeFromReference(this.MetadataReader, (TypeReferenceHandle)reference.Parent, 0); return GetFunctionWithSignature(containingType, name, signature); diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs index 0fcc0b05e..376a45b8f 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataSymbol.cs @@ -58,7 +58,7 @@ public static IEnumerable ToSymbol( foreach (var attributeHandle in typeDefinition.GetCustomAttributes()) { var attribute = reader.GetCustomAttribute(attributeHandle); - var typeProvider = new TypeProvider(compilation!); + var typeProvider = compilation.TypeProvider; switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs index a14ad8f2b..c69d01f3a 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/MetadataTypeSymbol.cs @@ -95,7 +95,7 @@ private ImmutableArray BuildGenericParameters() private ImmutableArray BuildBaseTypes() { var builder = ImmutableArray.CreateBuilder(); - var typeProvider = new TypeProvider(this.Assembly.Compilation); + var typeProvider = this.Assembly.Compilation.TypeProvider; if (!this.typeDefinition.BaseType.IsNil) { builder.Add(GetTypeFromMetadata(this.typeDefinition.BaseType)); diff --git a/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs b/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs index d0c071b6d..d9cab6d92 100644 --- a/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs +++ b/src/Draco.Compiler/Internal/Symbols/Metadata/TypeProvider.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Reflection; using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using Draco.Compiler.Api; using Draco.Compiler.Internal.Symbols.Synthetized; @@ -12,6 +14,8 @@ namespace Draco.Compiler.Internal.Symbols.Metadata; /// internal sealed class TypeProvider : ISignatureTypeProvider, ICustomAttributeTypeProvider { + private readonly record struct CacheKey(MetadataReader Reader, EntityHandle Handle); + // TODO: We return a special error type for now to swallow errors private static TypeSymbol UnknownType { get; } = new PrimitiveTypeSymbol("", false); @@ -19,6 +23,7 @@ internal sealed class TypeProvider : ISignatureTypeProvider, private IntrinsicSymbols IntrinsicSymbols => this.compilation.IntrinsicSymbols; private readonly Compilation compilation; + private readonly Dictionary cache = new(); public TypeProvider(Compilation compilation) { @@ -36,6 +41,7 @@ public TypeSymbol GetGenericInstantiation(TypeSymbol genericType, ImmutableArray if (ReferenceEquals(genericType, UnknownType)) return UnknownType; return genericType.GenericInstantiate(genericType.ContainingSymbol, typeArguments); } + public TypeSymbol GetGenericMethodParameter(Symbol genericContext, int index) { var methodAncestor = genericContext.AncestorChain @@ -46,6 +52,7 @@ public TypeSymbol GetGenericMethodParameter(Symbol genericContext, int index) ? methodAncestor.GenericParameters[index] : methodAncestor.GenericDefinition!.GenericParameters[index]; } + public TypeSymbol GetGenericTypeParameter(Symbol genericContext, int index) { var typeAncestor = genericContext.AncestorChain @@ -56,6 +63,7 @@ public TypeSymbol GetGenericTypeParameter(Symbol genericContext, int index) ? typeAncestor.GenericParameters[index] : typeAncestor.GenericDefinition!.GenericParameters[index]; } + public TypeSymbol GetModifiedType(TypeSymbol modifier, TypeSymbol unmodifiedType, bool isRequired) => UnknownType; public TypeSymbol GetPinnedType(TypeSymbol elementType) => UnknownType; public TypeSymbol GetPointerType(TypeSymbol elementType) => UnknownType; @@ -84,7 +92,19 @@ public TypeSymbol GetGenericTypeParameter(Symbol genericContext, int index) _ => UnknownType, }; + public TypeSymbol GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + { + var key = new CacheKey(reader, handle); + if (!this.cache.TryGetValue(key, out var type)) + { + type = this.BuildTypeFromDefinition(reader, handle, rawTypeKind); + this.cache.Add(key, type); + } + return type; + } + + private TypeSymbol BuildTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { var definition = reader.GetTypeDefinition(handle); if (definition.IsNested) @@ -96,12 +116,11 @@ public TypeSymbol GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHan // Search for this type by name and generic argument count var nestedName = reader.GetString(definition.Name); var nestedGenericArgc = definition.GetGenericParameters().Count; - var nestedSymbol = declaringSymbol + return declaringSymbol .DefinedMembers .OfType() .Where(t => t.Name == nestedName && t.GenericParameters.Length == nestedGenericArgc) .Single(); - return nestedSymbol; } var assemblyName = reader @@ -116,7 +135,19 @@ public TypeSymbol GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHan return this.WellKnownTypes.GetTypeFromAssembly(assemblyName, path); } + public TypeSymbol GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) + { + var key = new CacheKey(reader, handle); + if (!this.cache.TryGetValue(key, out var type)) + { + type = this.BuildTypeFromReference(reader, handle, rawTypeKind); + this.cache.Add(key, type); + } + return type; + } + + private TypeSymbol BuildTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var parts = new List(); var reference = reader.GetTypeReference(handle); @@ -136,6 +167,8 @@ public TypeSymbol GetTypeFromReference(MetadataReader reader, TypeReferenceHandl var assembly = this.compilation.MetadataAssemblies.Values.Single(x => x.AssemblyName.FullName == assemblyName.FullName); return assembly.RootNamespace.Lookup(parts.ToImmutableArray()).OfType().Single(); } + + // TODO: Should we cache this as well? doesn't seem to have any effect public TypeSymbol GetTypeFromSpecification(MetadataReader reader, Symbol genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { var specification = reader.GetTypeSpecification(handle);