diff --git a/.github/workflows/cecilifier-ci.yml b/.github/workflows/cecilifier-ci.yml index ec5e2169..ec5413eb 100644 --- a/.github/workflows/cecilifier-ci.yml +++ b/.github/workflows/cecilifier-ci.yml @@ -8,8 +8,8 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0.x' - - run: dotnet tool install --global dotnet-ilverify --version 9.0.0 + dotnet-version: '10.0.x' + - run: dotnet tool install --global dotnet-ilverify --version 10.0.12 - run: dotnet restore - run: dotnet test --filter 'TestCategory !~ Issues' --collect:"XPlat Code Coverage" - run: dotnet format style --verify-no-changes diff --git a/Cecilifier.ApiDriver.MonoCecil/Cecilifier.ApiDriver.MonoCecil.csproj b/Cecilifier.ApiDriver.MonoCecil/Cecilifier.ApiDriver.MonoCecil.csproj index 17843aa8..8f2abb64 100644 --- a/Cecilifier.ApiDriver.MonoCecil/Cecilifier.ApiDriver.MonoCecil.csproj +++ b/Cecilifier.ApiDriver.MonoCecil/Cecilifier.ApiDriver.MonoCecil.csproj @@ -1,7 +1,8 @@  + + - net9.0 enable enable diff --git a/Cecilifier.ApiDriver.MonoCecil/MonoCecilContext.cs b/Cecilifier.ApiDriver.MonoCecil/MonoCecilContext.cs index 9246d888..bbfe1b26 100644 --- a/Cecilifier.ApiDriver.MonoCecil/MonoCecilContext.cs +++ b/Cecilifier.ApiDriver.MonoCecil/MonoCecilContext.cs @@ -24,7 +24,8 @@ public MonoCecilContext(CecilifierOptions options, SemanticModel semanticModel, public static IVisitorContext CreateContext(CecilifierOptions options, SemanticModel semanticModel) => new MonoCecilContext(options, semanticModel); public static string[] BclAssembliesForCompilation() { - return ((string) AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")).Split(Path.PathSeparator); + var data = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES"); + return data != null ? ((string) data).Split(Path.PathSeparator) : Array.Empty(); } public static string[] GetPreprocessorSymbols() => ["API_DRIVER_MONO_CECIL"]; diff --git a/Cecilifier.ApiDriver.MonoCecil/MonoCecilDeferredIlContext.cs b/Cecilifier.ApiDriver.MonoCecil/MonoCecilDeferredIlContext.cs index f742bd30..55fa33f0 100644 --- a/Cecilifier.ApiDriver.MonoCecil/MonoCecilDeferredIlContext.cs +++ b/Cecilifier.ApiDriver.MonoCecil/MonoCecilDeferredIlContext.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Cecilifier.Core.AST; namespace Cecilifier.ApiDriver.MonoCecil; @@ -6,25 +7,42 @@ public class MonoCecilDeferredIlContext : IlContext { private readonly IVisitorContext _context; private bool _emitted; +#if DEBUG_MATERIALIZATION_CHECK + private string _debugInfo; +#endif protected internal MonoCecilDeferredIlContext(IVisitorContext context, string ilVarName, string relatedMethodVar) : base(ilVarName, relatedMethodVar) { _context = context; _emitted = false; + +#if DEBUG_MATERIALIZATION_CHECK + _debugInfo = new StackTrace(true).ToString(); +#endif } public override string VariableName { get { +#if DEBUG_MATERIALIZATION_CHECK if (!_emitted) - { - _emitted = true; - _context.Generate($"var {base.VariableName} = {AssociatedMethodVariable}.Body.GetILProcessor();"); - _context.WriteNewLine(); - } + throw new InvalidOperationException($"IL context is not materialized yet\nIlContext instantiated at:\n{_debugInfo}\n----------------------------"); +#else + Materialize(); +#endif return base.VariableName; } } + + public override void Materialize() + { + if (!_emitted) + { + _emitted = true; + _context.Generate($"var {base.VariableName} = {AssociatedMethodVariable}.Body.GetILProcessor();"); + _context.WriteNewLine(); + } + } } diff --git a/Cecilifier.ApiDriver.MonoCecil/MonoCecilDefinitionsFactory.cs b/Cecilifier.ApiDriver.MonoCecil/MonoCecilDefinitionsFactory.cs index b2b30df1..e56a69a1 100644 --- a/Cecilifier.ApiDriver.MonoCecil/MonoCecilDefinitionsFactory.cs +++ b/Cecilifier.ApiDriver.MonoCecil/MonoCecilDefinitionsFactory.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -15,6 +16,9 @@ using Cecilifier.Core.Naming; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; +using Mono.Cecil; +using CustomAttributeArgument = Cecilifier.Core.ApiDriver.Attributes.CustomAttributeArgument; +using CustomAttributeNamedArgument = Cecilifier.Core.ApiDriver.Attributes.CustomAttributeNamedArgument; namespace Cecilifier.ApiDriver.MonoCecil; @@ -47,7 +51,7 @@ public IEnumerable Type( ResolvedType resolvedBaseType = baseType == null || (baseType is INamedTypeSymbol { IsGenericType: true } genericInstance && genericInstance.TypeArguments.Any(t => t.TypeKind == TypeKind.TypeParameter)) ? null - : context.TypeResolver.ResolveAny(baseType, ResolveTargetKind.TypeReference); + : context.TypeResolver.Resolve(baseType, ResolveTargetKind.TypeReference); var typeDefExp = $"var {typeVar} = new TypeDefinition(\"{typeNamespace}\", \"{typeName}\", {attrs}{(resolvedBaseType != null ? $", {resolvedBaseType}" : "")})"; if (properties.Length > 0) @@ -64,7 +68,7 @@ public IEnumerable Type( foreach (var itf in interfaces) { - exps.Add($"{typeVar}.Interfaces.Add(new InterfaceImplementation({context.TypeResolver.ResolveAny(itf, ResolveTargetKind.TypeReference)}));"); + exps.Add($"{typeVar}.Interfaces.Add(new InterfaceImplementation({context.TypeResolver.Resolve(itf, ResolveTargetKind.TypeReference)}));"); } if (definitionContext.ParentDefinitionVariable != null) @@ -88,7 +92,7 @@ public void UpdateBaseTypeIfNeeded(IVisitorContext context, ITypeSymbol typeSymb if (typeSymbol.BaseType is not { IsGenericType: true }) return; - var resolvedBaseType = context.TypeResolver.ResolveAny(typeSymbol.BaseType, ResolveTargetKind.TypeReference); + var resolvedBaseType = context.TypeResolver.Resolve(typeSymbol.BaseType, ResolveTargetKind.TypeReference); context.Generate($"{typeDefinitionVariable}.BaseType = {resolvedBaseType};"); } @@ -96,10 +100,10 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS { var exps = new List(); - var resolvedReturnType = context.TypeResolver.ResolveAny(methodSymbol.ReturnType, methodSymbol.ToTypeResolutionContext()); + var resolvedReturnType = context.TypeResolver.Resolve(methodSymbol.ReturnType, methodSymbol.ToTypeResolutionContext()); var refReturn = methodSymbol.ReturnsByRef || methodSymbol.ReturnsByRefReadonly; if (refReturn) - resolvedReturnType = resolvedReturnType.MakeByReferenceType(); + resolvedReturnType = context.TypeResolver.MakeByRefType(resolvedReturnType); // for type parameters we may need to postpone setting the return type (using void as a placeholder, since we need to pass something) until the generic parameters has been // handled. This is required because the type parameter may be defined by the method being processed. @@ -107,8 +111,8 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS ProcessGenericTypeParameters(bodiedMemberDefinitionContext.Member.DefinitionVariable, context, typeParameters, exps); if (methodSymbol.ReturnType.IsTypeParameterOrIsGenericTypeReferencingTypeParameter()) { - resolvedReturnType = context.TypeResolver.ResolveAny(methodSymbol.ReturnType, methodSymbol.ToTypeResolutionContext()); - exps.Add($"{bodiedMemberDefinitionContext.Member.DefinitionVariable}.ReturnType = {(refReturn ? resolvedReturnType.MakeByReferenceType() : resolvedReturnType)};"); + resolvedReturnType = context.TypeResolver.Resolve(methodSymbol.ReturnType, methodSymbol.ToTypeResolutionContext()); + exps.Add($"{bodiedMemberDefinitionContext.Member.DefinitionVariable}.ReturnType = {(refReturn ? context.TypeResolver.MakeByRefType(resolvedReturnType) : resolvedReturnType)};"); } exps.Add($"{context.DefinitionVariables.GetLastOf(VariableMemberKind.Type).VariableName}.Methods.Add({bodiedMemberDefinitionContext.Member.DefinitionVariable});"); @@ -147,12 +151,13 @@ public IEnumerable Method(IVisitorContext context, { var paramVar = context.Naming.SyntheticVariable(parameter.Name, ElementKind.Parameter); var parameterExp = CecilDefinitionsFactory.Parameter( + context, parameter.Name, parameter.RefKind, parameter.ParamsAttributeName, // for now,the only callers for this method don't have any `params` parameters. definitionContext.Member.DefinitionVariable, paramVar, - parameter.ElementTypeResolver != null ? parameter.ElementTypeResolver(context, parameter.ElementType.Expression) : parameter.ElementType, + parameter.ElementTypeResolver != null ? parameter.ElementTypeResolver(context, parameter) : parameter.ElementType, parameter.Attributes, (parameter.DefaultValue, parameter.DefaultValue != null)); @@ -162,7 +167,7 @@ public IEnumerable Method(IVisitorContext context, if (definitionContext.Member.ParentDefinitionVariable != null) { - methodVariable = context.DefinitionVariables.RegisterMethod(declaringTypeName, definitionContext.Member.Name, parameters.Select(p => p.RegistrationTypeName).ToArray(), typeParameters.Count, definitionContext.Member.DefinitionVariable); + methodVariable = context.DefinitionVariables.RegisterMethod(declaringTypeName, definitionContext.Member.Name, parameters.Select(p => p.RegistrationTypeName).ToArray()!, typeParameters.ToArray(), definitionContext.Member.DefinitionVariable); exps = [ ..exps, @@ -231,7 +236,7 @@ string OperandFor(InstructionRepresentation inst) CilToken token => token.VariableName, string str => str, DefinitionVariable definitionVariable => definitionVariable.VariableName, - _ => (string) inst.Operand + _ => inst.Operand.ToString()! }; return $", {operandValue}"; @@ -245,7 +250,7 @@ string OperandFor(InstructionRepresentation inst) public IEnumerable Constructor(IVisitorContext context, BodiedMemberDefinitionContext definitionContext, string typeName, bool isStatic, string methodAccessibility, string[] paramTypes, string? methodDefinitionPropertyValues = null) { var ctorName = Utils.ConstructorMethodName(isStatic); - context.DefinitionVariables.RegisterMethod(typeName, ctorName, paramTypes, 0, definitionContext.Member.DefinitionVariable); + context.DefinitionVariables.RegisterMethod(typeName, ctorName, paramTypes, [], definitionContext.Member.DefinitionVariable); var exp = $@"var {definitionContext.Member.DefinitionVariable} = new MethodDefinition(""{ctorName}"", {methodAccessibility} | MethodAttributes.HideBySig | {Constants.Cecil.CtorAttributes}, assembly.MainModule.TypeSystem.Void)"; if (methodDefinitionPropertyValues != null) @@ -258,13 +263,13 @@ public IEnumerable Constructor(IVisitorContext context, BodiedMemberDefi public IEnumerable Field(IVisitorContext context, in MemberDefinitionContext definitionContext, ISymbol fieldOrEvent, ITypeSymbol fieldType, string fieldAttributes, bool isVolatile, bool isByRef, in FieldInitializationData initializer = default) { - return Field(context, definitionContext, fieldOrEvent.ContainingType.ToDisplayString(), context.TypeResolver.ResolveAny(fieldType, ResolveTargetKind.Field), fieldAttributes, isVolatile, isByRef, initializer); + return Field(context, definitionContext, fieldOrEvent.ContainingType.ToDisplayString(), context.TypeResolver.Resolve(fieldType, ResolveTargetKind.Field), fieldAttributes, isVolatile, isByRef, initializer); } public IEnumerable Field(IVisitorContext context, MemberDefinitionContext definitionContext, string declaringTypeName, ResolvedType fieldType, string fieldAttributes, bool isVolatile, bool isByRef, FieldInitializationData initializer = default) { if (isByRef) - fieldType = fieldType.MakeByReferenceType(); + fieldType = context.TypeResolver.MakeByRefType(fieldType); context.DefinitionVariables.RegisterNonMethod(declaringTypeName, definitionContext.Name, VariableMemberKind.Field, definitionContext.DefinitionVariable); @@ -325,7 +330,7 @@ public IEnumerable Attribute(IVisitorContext context, IMethodSymbol attr for(int i = 0; i < positionalArguments.Length; i++) { - var attributeArgument = $"new CustomAttributeArgument({context.TypeResolver.ResolveAny(attributeCtor.Parameters[i].Type.OriginalDefinition, ResolveTargetKind.TypeReference)}, {CustomAttributeArgumentValueFor(context, positionalArguments[i].Value)})"; + var attributeArgument = $"new CustomAttributeArgument({context.TypeResolver.Resolve(attributeCtor.Parameters[i].Type.OriginalDefinition, ResolveTargetKind.TypeReference)}, {CustomAttributeArgumentValueFor(context, positionalArguments[i].Value!)})"; exps[expIndex++] = $"{attributeVar}.ConstructorArguments.Add({attributeArgument});"; } expIndex += ProcessAttributeNamedArguments(context, exps.Slice(expIndex), attributeVar, namedArguments); @@ -340,13 +345,157 @@ static int ProcessAttributeNamedArguments(IVisitorContext context, Span foreach (var namedArgument in namedArguments) { var container = (namedArgument.Kind == NamedArgumentKind.Field ? "Fields" : "Properties"); - var value = $"new CustomAttributeArgument({namedArgument.ResolvedType}, {CustomAttributeArgumentValueFor(context, namedArgument.Value)})"; + var value = $"new CustomAttributeArgument({namedArgument.ResolvedType}, {CustomAttributeArgumentValueFor(context, namedArgument.Value!)})"; exps[i++] = $"{customAttrVariable}.{container}.Add(new CustomAttributeNamedArgument(\"{namedArgument.Name}\", {value}));"; } return i; } } + public IEnumerable Event(IVisitorContext context, BodiedMemberDefinitionContext eventSpec, string declaringTypeName, ResolvedType eventType, string addAccessorVariable, string removeAccessorVariable) + { + var evtDefVar = eventSpec.Member.DefinitionVariable; + + return + [ + $"var {evtDefVar} = new EventDefinition(\"{eventSpec.Member.Name}\", EventAttributes.None, {eventType.Expression});", + $"{evtDefVar}.AddMethod = {addAccessorVariable};", + $"{evtDefVar}.RemoveMethod = {removeAccessorVariable};", + $"{eventSpec.Member.ParentDefinitionVariable}.Events.Add({evtDefVar});" + ]; + } + + public void OverrideBaseMethod(IVisitorContext context, string overriderMethodVar, string? overridenMethod) + { + if (overridenMethod == null) + return; + + context.Generate($"{overriderMethodVar}.Overrides.Add({overridenMethod});"); + context.WriteNewLine(); + } + + public IEnumerable PInvoke(IVisitorContext context, string moduleName, string methodVar, string methodName, ReadOnlySpan customAttributeArguments) + { + var existingModuleVar = context.DefinitionVariables.GetVariable(moduleName, VariableMemberKind.ModuleReference); + + var moduleVar = existingModuleVar.IsValid + ? existingModuleVar.VariableName + : context.Naming.SyntheticVariable("dllImportModule", ElementKind.LocalVariable); + + var exps = new List(); + if (!existingModuleVar.IsValid) + { + exps.AddRange([ + $"""var {moduleVar} = new ModuleReference("{moduleName}");""", + $"assembly.MainModule.ModuleReferences.Add({moduleVar});" + ]); + } + + exps.AddRange([ + $"{methodVar}.PInvokeInfo = new PInvokeInfo({ PInvokeAttributesFrom(customAttributeArguments) }, { EntryPoint(customAttributeArguments) }, {moduleVar});", + $"{methodVar}.Body = null;", + $"{methodVar}.ImplAttributes = {MethodImplAttributes(customAttributeArguments)};", + ]); + + context.DefinitionVariables.RegisterNonMethod("", moduleName, VariableMemberKind.ModuleReference, moduleVar); + + return exps; + + static string EntryPoint(ReadOnlySpan customAttributeArguments) => $""" + "{AttributePropertyOrDefaultValue(customAttributeArguments, "EntryPoint","") }" + """; + + static string MethodImplAttributes(ReadOnlySpan customAttributeArguments) + { + var preserveSig = Boolean.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "PreserveSig", "true")); + + return preserveSig + ? "MethodImplAttributes.PreserveSig | MethodImplAttributes.Managed" + : "MethodImplAttributes.Managed"; + } + + static StringBuilder CallingConventionFrom(ReadOnlySpan customAttributeArguments) + { + var callConventionSpan = AttributePropertyOrDefaultValue(customAttributeArguments, "CallingConvention", "Winapi").AsSpan(); + + // ensures we use the enum member simple name; Parse() fails if we pass a qualified enum member + var index = callConventionSpan.LastIndexOf('.'); + callConventionSpan = callConventionSpan.Slice(index + 1); + + return new StringBuilder(CallingConventionToCecil(Enum.Parse(callConventionSpan))); + } + + static string CharSetFrom(ReadOnlySpan customAttributeArguments) + { + var enumMemberName = AttributePropertyOrDefaultValue(customAttributeArguments, "CharSet", "None").AsSpan(); + + // Only use the actual enum member name Parse() fails if we pass a qualified enum member + var index = enumMemberName.LastIndexOf('.'); + enumMemberName = enumMemberName.Slice(index + 1); + + var charSet = Enum.Parse(enumMemberName); + return charSet == CharSet.None ? string.Empty : $"PInvokeAttributes.CharSet{charSet}"; + } + + static string SetLastErrorFrom(ReadOnlySpan customAttributeArguments) + { + var setLastError = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "SetLastError", "false")); + return setLastError ? "PInvokeAttributes.SupportsLastError" : string.Empty; + } + + static string ExactSpellingFrom(ReadOnlySpan customAttributeArguments) + { + var exactSpelling = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "ExactSpelling", "false")); + return exactSpelling ? "PInvokeAttributes.NoMangle" : string.Empty; + } + + static string BestFitMappingFrom(ReadOnlySpan customAttributeArguments) + { + var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "BestFitMapping", "true")); + return bestFitMapping ? "PInvokeAttributes.BestFitEnabled" : "PInvokeAttributes.BestFitDisabled"; + } + + static string ThrowOnUnmappableCharFrom(ReadOnlySpan customAttributeArguments) + { + var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "ThrowOnUnmappableChar", "false")); + return bestFitMapping ? "PInvokeAttributes.ThrowOnUnmappableCharEnabled" : "PInvokeAttributes.ThrowOnUnmappableCharDisabled"; + } + + // For more information and default values see + // https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute + static string PInvokeAttributesFrom(ReadOnlySpan customAttributeArguments) + { + return CallingConventionFrom(customAttributeArguments) + .AppendEnumFlag(CharSetFrom(customAttributeArguments)) + .AppendEnumFlag(SetLastErrorFrom(customAttributeArguments)) + .AppendEnumFlag(ExactSpellingFrom(customAttributeArguments)) + .AppendEnumFlag(BestFitMappingFrom(customAttributeArguments)) + .AppendEnumFlag(ThrowOnUnmappableCharFrom(customAttributeArguments)) + .ToString(); + } + + static string AttributePropertyOrDefaultValue(ReadOnlySpan customAttributeArguments, string propertyName, string defaultValue) + { + return customAttributeArguments.ToArray().OfType().FirstOrDefault(arg => arg.Name == propertyName)?.Value?.ToString() ?? defaultValue; + } + + static string CallingConventionToCecil(CallingConvention callingConvention) + { + var pinvokeAttribute = callingConvention switch + { + CallingConvention.Cdecl => PInvokeAttributes.CallConvCdecl, + CallingConvention.Winapi => PInvokeAttributes.CallConvWinapi, + CallingConvention.FastCall => PInvokeAttributes.CallConvFastcall, + CallingConvention.StdCall => PInvokeAttributes.CallConvStdCall, + CallingConvention.ThisCall => PInvokeAttributes.CallConvThiscall, + + _ => throw new Exception($"Unexpected calling convention: {callingConvention}") + }; + + return $"PInvokeAttributes.{pinvokeAttribute.ToString()}"; + } + } + private static string CustomAttributeArgumentValueFor(IVisitorContext context, object argument) { if (argument is Array array) @@ -357,7 +506,7 @@ private static string CustomAttributeArgumentValueFor(IVisitorContext context, o var sb = new StringBuilder("new [] {"); foreach (CustomAttributeArgument arrayItem in array) { - sb.Append($"new CustomAttributeArgument({context.TypeResolver.Resolve(context.RoslynTypeSystem.ForType(arrayItem.Value.GetType().FullName), ResolveTargetKind.TypeReference)}, {CustomAttributeArgumentValueFor(context, arrayItem.Value)}), "); + sb.Append($"new CustomAttributeArgument({context.TypeResolver.Resolve(context.RoslynTypeSystem.ForType(arrayItem.Value!.GetType().FullName), ResolveTargetKind.TypeReference)}, {CustomAttributeArgumentValueFor(context, arrayItem.Value)}), "); } sb.Remove(sb.Length - 2, 2); // Removes the last ", " sb.Append('}'); @@ -405,9 +554,9 @@ void AddConstraints(string genParamDefVar, ITypeParameterSymbol typeParam, TypeP if (typeParam.HasValueTypeConstraint) { - var systemValueTypeRef = Utils.ImportFromMainModule("typeof(System.ValueType)"); + var systemValueTypeRef = context.TypeResolver.Bcl.System.ValueType; var constraintType = typeParam.HasUnmanagedTypeConstraint - ? $"{systemValueTypeRef}.MakeRequiredModifierType({context.TypeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference)})" + ? $"{systemValueTypeRef}.MakeRequiredModifierType({context.TypeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference)})" : systemValueTypeRef; exps.Add($"{genParamDefVar}.Constraints.Add(new GenericParameterConstraint({constraintType}));"); @@ -434,7 +583,7 @@ void AddConstraints(string genParamDefVar, ITypeParameterSymbol typeParam, TypeP foreach (var type in typeParam.ConstraintTypes) { - exps.Add($"{genParamDefVar}.Constraints.Add(new GenericParameterConstraint({context.TypeResolver.ResolveAny(type, ResolveTargetKind.TypeReference)}));"); + exps.Add($"{genParamDefVar}.Constraints.Add(new GenericParameterConstraint({context.TypeResolver.Resolve(type, ResolveTargetKind.TypeReference)}));"); } } } diff --git a/Cecilifier.ApiDriver.MonoCecil/MonoCecilGeneratorDriver.cs b/Cecilifier.ApiDriver.MonoCecil/MonoCecilGeneratorDriver.cs index 80168a36..b35b86c5 100644 --- a/Cecilifier.ApiDriver.MonoCecil/MonoCecilGeneratorDriver.cs +++ b/Cecilifier.ApiDriver.MonoCecil/MonoCecilGeneratorDriver.cs @@ -6,6 +6,7 @@ using Cecilifier.Core.AST; using Cecilifier.Core.Extensions; using Cecilifier.Core.TypeSystem; +using Cecilifier.Core.Variables; using Microsoft.CodeAnalysis; namespace Cecilifier.ApiDriver.MonoCecil; @@ -54,6 +55,8 @@ public static void Main(string[] args) }}"; } + public ApiDriverCapabilities DriverCapabilities => ApiDriverCapabilities.RequiresForwardReferences | ApiDriverCapabilities.RequiresExplicitParameterSyntaxHandling; + public int PreambleLineCount => 25; // The # of lines before the 1st cecilified line of code (see `cecilifiedCode` parameter from AsCecilApplication()) public IReadOnlyCollection AssemblyReferences { get; } = @@ -91,7 +94,13 @@ public void WriteCilInstruction(IVisitorContext context, IlContext il, OpCode op public void WriteCilBranch(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null) { - WriteCilInstruction(context, il, branchOpCode, targetLabel, comment); + context.Generate(EmitCilInstruction(context, il, branchOpCode, targetLabel, comment)); + context.WriteNewLine(); + } + + public string EmitCilBranchInstruction(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null) + { + return EmitCilInstruction(context, il, branchOpCode, targetLabel, comment); } public void DefineLabel(IVisitorContext context, IlContext il, string labelVariable) @@ -99,13 +108,17 @@ public void DefineLabel(IVisitorContext context, IlContext il, string labelVaria context.Generate($"var {labelVariable} = {il.VariableName}.Create(OpCodes.Nop);"); context.WriteNewLine(); } - + + public string EmitDefineLabel(IVisitorContext context, IlContext il, string labelVariable) => $"var {labelVariable} = {il.VariableName}.Create(OpCodes.Nop);"; + public void MarkLabel(IVisitorContext context, IlContext il, string labelVariable) { context.Generate($"{il.VariableName}.Append({labelVariable});"); context.WriteNewLine(); } + public string EmitMarkLabel(IVisitorContext context, IlContext il, string labelVariable) => $"{il.VariableName}.Append({labelVariable});"; + public IlContext NewIlContext(IVisitorContext context, string memberName, string relatedMethodVar) { var ilVarName = context.Naming.ILProcessor(memberName); @@ -128,4 +141,32 @@ public void AddMethodSemantics(IVisitorContext context, string targetVariable, s $"{methodVariable}.Body = new MethodBody({methodVariable});", $"{targetVariable}.{accessor} = {methodVariable};" ]); } + + public void WriteExceptionHandlers(IVisitorContext context, IlContext ilVar, IEnumerable exceptionHandlerTable) + { + string methodVar = context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); + foreach (var handlerEntry in exceptionHandlerTable) + { + context.Generate($"{methodVar}.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.{handlerEntry.Kind})"); + context.WriteNewLine(); + context.Generate("{"); + context.WriteNewLine(); + if (handlerEntry.Kind == ExceptionHandlerKind.Catch) + { + context.Generate($" CatchType = {handlerEntry.CatchType},"); + context.WriteNewLine(); + } + + context.Generate($" TryStart = {handlerEntry.TryStart},"); + context.WriteNewLine(); + context.Generate($" TryEnd = {handlerEntry.TryEnd},"); + context.WriteNewLine(); + context.Generate($" HandlerStart = {handlerEntry.HandlerStart},"); + context.WriteNewLine(); + context.Generate($" HandlerEnd = {handlerEntry.HandlerEnd}"); + context.WriteNewLine(); + context.Generate("});"); + context.WriteNewLine(); + } + } } diff --git a/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilMemberResolver.cs b/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilMemberResolver.cs index 670c91e1..1b9a9d69 100644 --- a/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilMemberResolver.cs +++ b/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilMemberResolver.cs @@ -6,6 +6,7 @@ using Cecilifier.Core.Extensions; using Cecilifier.Core.Misc; using Cecilifier.Core.Naming; +using Cecilifier.Core.Services; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; using Microsoft.CodeAnalysis; @@ -28,7 +29,7 @@ public string ResolveMethod(IMethodSymbol method) if (!method.ContainingType.IsGenericType) return found.VariableName.MakeGenericInstanceMethod(context, method); - var declaringType = context.TypeResolver.ResolveAny(method.ContainingType, ResolveTargetKind.TypeReference); + var declaringType = context.TypeResolver.Resolve(method.ContainingType, ResolveTargetKind.TypeReference); var exps = found.VariableName.CloneMethodReferenceOverriding(context, new() { ["DeclaringType"] = declaringType.Expression }, method, out var variable); context.Generate(exps); return variable.MakeGenericInstanceMethod(context, method); @@ -47,10 +48,10 @@ public string ResolveMethod(IMethodSymbol method) if (method.IsGenericMethod) { var (referencedMethodTypeParameters, returnReferencesTypeParameter) = CollectReferencedMethodTypeParameters(method); - var returnType = !returnReferencesTypeParameter ? context.TypeResolver.ResolveAny(method.ReturnType, ResolveTargetKind.TypeReference) : context.TypeResolver.Bcl.System.Void; + var returnType = !returnReferencesTypeParameter ? context.TypeResolver.Resolve(method.ReturnType, ResolveTargetKind.TypeReference) : context.TypeResolver.Bcl.System.Void; var tempMethodVar = context.Naming.SyntheticVariable(method.Name, ElementKind.MemberReference); context.Generate($$""" - var {{tempMethodVar}} = new MethodReference("{{method.Name}}", {{returnType}}, {{context.TypeResolver.ResolveAny(method.ContainingType, ResolveTargetKind.TypeReference)}}) + var {{tempMethodVar}} = new MethodReference("{{method.Name}}", {{returnType}}, {{context.TypeResolver.Resolve(method.ContainingType, ResolveTargetKind.TypeReference)}}) { HasThis = {{(!method.IsStatic).ToKeyword()}}, ExplicitThis = false, @@ -85,12 +86,12 @@ public string ResolveMethod(IMethodSymbol method) if (returnReferencesTypeParameter) { - var resolvedReturnType = context.TypeResolver.ResolveAny(method.OriginalDefinition.ReturnType, method.OriginalDefinition.ToTypeResolutionContext(tempMethodVar)); + var resolvedReturnType = context.TypeResolver.Resolve(method.OriginalDefinition.ReturnType, method.OriginalDefinition.ToTypeResolutionContext(tempMethodVar)); // the information about the type being passed as `ref` is not in the ITypeSymbol so we need to check and produce // a Mono.Cecil.ByReferenceType if (method.ReturnsByRef || method.ReturnsByRefReadonly) - resolvedReturnType = resolvedReturnType.MakeByReferenceType(); + resolvedReturnType = context.TypeResolver.MakeByRefType(resolvedReturnType); context.Generate($"{tempMethodVar}.ReturnType = {resolvedReturnType};"); context.WriteNewLine(); @@ -105,9 +106,10 @@ public string ResolveMethod(IMethodSymbol method) toDispose.ForEach(v => v.Dispose()); + IReadOnlyList resolvedTypeArguments = method.TypeArguments.Select(t => context.TypeResolver.Resolve(t, ResolveTargetKind.TypeReference)).ToList(); return method.IsDefinition ? tempMethodVar - : tempMethodVar.MakeGenericInstanceMethod(context, method.Name, method.TypeArguments.Select(t => context.TypeResolver.ResolveAny(t, ResolveTargetKind.TypeReference)).ToList()); + : context.MemberResolver.MakeGeneticInstanceMethod(tempMethodVar, method.Name, resolvedTypeArguments); } if (method.Parameters.Any(p => p.Type.IsTypeParameterOrIsGenericTypeReferencingTypeParameter()) @@ -117,13 +119,42 @@ public string ResolveMethod(IMethodSymbol method) return ResolveMethodFromGenericType(method, context); } - return Utils.ImportFromMainModule( - $"TypeHelpers.ResolveMethod(typeof({declaringTypeName}), \"{method.Name}\",{ReflectionBindingsFlags(method)}{method.Parameters.Aggregate("", (acc, curr) => acc + ", \"" + curr.Type.GetReflectionName() + "\"")})"); + return ((MonoCecilTypeResolver) context.TypeResolver).ImportReference( + $"TypeHelpers.ResolveMethod(typeof({declaringTypeName}), \"{method.Name}\",{ReflectionBindingsFlags(method)}{method.Parameters.Aggregate("", (acc, curr) => acc + ", \"" + curr.Type.GetReflectionName() + "\"")})").Expression; } - public string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodNameForVariableRegistration, ResolvedType returnType, IReadOnlyList parameters, int typeParameterCountCount, MemberOptions options) + public string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodName, ResolvedType returnType, IReadOnlyList parameters, IReadOnlyList typeParameters, MemberOptions options) { - throw new NotImplementedException(); + // Unfortunately here we don't have enough information about the method and its declaring type. If the declaring type is a generic type instance, the generated code need to import the method + // and by doing that the generic type information is lost. To fix this we need to set the `DeclaringType` of the method to the correct generic instance type. + // most likely there's a better way achieve this but as of today I don't know how (maybe use the ImportReference(MethodReference, IGenericParameterProvider) ?) + var correctDeclaringType = context.Naming.SyntheticVariable("declaringType", ElementKind.Method); + context.Generate($"""var {correctDeclaringType} = {declaringTypeVariable};"""); + context.WriteNewLine(); + + var methodWithIncorrectDeclaringType = context.Naming.SyntheticVariable("tmpMethod", ElementKind.MemberReference); + context.Generate($"""var {methodWithIncorrectDeclaringType} = assembly.MainModule.ImportReference({correctDeclaringType}.ElementType.Resolve().Methods.Single(m => m.Name == "{methodName}" && m.Parameters.Count == {parameters.Count}));"""); + context.WriteNewLine(); + + var variable = context.Naming.SyntheticVariable("genericMethod", ElementKind.MemberReference); + context.Generate($$""" + var {{variable}} = new MethodReference({{methodWithIncorrectDeclaringType}}.Name, {{methodWithIncorrectDeclaringType}}.ReturnType) + { + HasThis = {{methodWithIncorrectDeclaringType}}.HasThis, + CallingConvention = {{methodWithIncorrectDeclaringType}}.CallingConvention, + ExplicitThis = {{methodWithIncorrectDeclaringType}}.ExplicitThis, + DeclaringType = {{methodWithIncorrectDeclaringType}}.DeclaringType.MakeGenericInstanceType({{correctDeclaringType}}.GenericArguments.ToArray()), + }; + """); + context.WriteNewLine(); + + for (int i = 0; i < parameters.Count; i++) + { + context.Generate($"""{variable}.Parameters.Add(new ParameterDefinition({methodWithIncorrectDeclaringType}.Parameters[{i}].Name, {methodWithIncorrectDeclaringType}.Parameters[{i}].Attributes, {methodWithIncorrectDeclaringType}.Parameters[{i}].ParameterType));"""); + context.WriteNewLine(); + } + + return variable; } public string ResolveDefaultConstructor(ITypeSymbol baseType, string derivedTypeVar) @@ -134,7 +165,7 @@ public string ResolveDefaultConstructor(ITypeSymbol baseType, string derivedType return $"new MethodReference(\".ctor\", {context.TypeResolver.Bcl.System.Void} ,{baseTypeVarDef}) {{ HasThis = true }}"; } - return Utils.ImportFromMainModule($"TypeHelpers.DefaultCtorFor({derivedTypeVar}.BaseType)"); + return ImportReference($"TypeHelpers.DefaultCtorFor({derivedTypeVar}.BaseType)"); } private static (HashSet, bool) CollectReferencedMethodTypeParameters(IMethodSymbol method) @@ -174,18 +205,21 @@ static bool ReferencesTypeParameter(ITypeSymbol toCheck, ITypeParameterSymbol ty } } - private static string ResolveMethodFromGenericType(IMethodSymbol method, IVisitorContext _context) + private static string ResolveMethodFromGenericType(IMethodSymbol method, IVisitorContext context) { // resolve declaring type of the method. - var targetTypeVarName = _context.Naming.SyntheticVariable($"{method.ContainingType.Name}", ElementKind.LocalVariable); - var resolvedTargetTypeExp = _context.TypeResolver.ResolveAny(method.ContainingType.OriginalDefinition, ResolveTargetKind.TypeReference) - .MakeGenericInstanceType( - method.ContainingType.GetAllTypeArguments().Select(t => _context.TypeResolver.ResolveAny(t, ResolveTargetKind.TypeReference))); - _context.Generate($"var {targetTypeVarName} = {resolvedTargetTypeExp};"); - _context.WriteNewLine(); + var targetTypeVarName = context.Naming.SyntheticVariable($"{method.ContainingType.Name}", ElementKind.LocalVariable); + var resolvedTargetTypeExp = context.TypeResolver.MakeGenericInstanceType( + method.ContainingType.OriginalDefinition.NameIncludingTypeParametersAndArguments(), + context.TypeResolver.Resolve(method.ContainingType.OriginalDefinition, ResolveTargetKind.TypeReference), + method.ContainingType, + ResolveTargetKind.None); + + context.Generate($"var {targetTypeVarName} = {resolvedTargetTypeExp};"); + context.WriteNewLine(); // find the original method. - var originalMethodVar = _context.Naming.SyntheticVariable($"open{method.Name}", ElementKind.LocalVariable); + var originalMethodVar = context.Naming.SyntheticVariable($"open{method.Name}", ElementKind.LocalVariable); var methodParameterNames = method.Parameters.Aggregate(new StringBuilder(), (acc, curr) => acc.Append($""" "{curr.Type.FullyQualifiedName()}", @@ -194,13 +228,13 @@ private static string ResolveMethodFromGenericType(IMethodSymbol method, IVisito ? $" && !m.Parameters.Select(p => p.ParameterType.FullName).Except([{methodParameterNames}]).Any()" : string.Empty; - _context.Generate( - $"""var {originalMethodVar} = {_context.TypeResolver.ResolveAny(method.ContainingType.OriginalDefinition, ResolveTargetKind.TypeReference)}.Resolve().Methods.First(m => m.Name == "{method.Name}" && m.Parameters.Count == {method.Parameters.Length}{parameterTypesCheck});"""); - _context.WriteNewLine(); + context.Generate( + $"""var {originalMethodVar} = {context.TypeResolver.Resolve(method.ContainingType.OriginalDefinition, ResolveTargetKind.TypeReference)}.Resolve().Methods.First(m => m.Name == "{method.Name}" && m.Parameters.Count == {method.Parameters.Length}{parameterTypesCheck});"""); + context.WriteNewLine(); // Instantiates a MethodReference representing the called method. - var targetMethodVar = _context.Naming.SyntheticVariable($"{method.Name}", ElementKind.MemberReference); - _context.Generate( + var targetMethodVar = context.Naming.SyntheticVariable($"{method.Name}", ElementKind.MemberReference); + context.Generate( $$""" var {{targetMethodVar}} = new MethodReference("{{method.Name}}", assembly.MainModule.ImportReference({{originalMethodVar}}).ReturnType) { @@ -210,14 +244,14 @@ private static string ResolveMethodFromGenericType(IMethodSymbol method, IVisito CallingConvention = {{originalMethodVar}}.CallingConvention, }; """); - _context.WriteNewLine(); + context.WriteNewLine(); // Add original parameters to the MethodReference foreach (var parameter in method.Parameters) { - _context.Generate( + context.Generate( $"""{targetMethodVar}.Parameters.Add(new ParameterDefinition("{parameter.Name}", {originalMethodVar}.Parameters[{parameter.Ordinal}].Attributes, {originalMethodVar}.Parameters[{parameter.Ordinal}].ParameterType));"""); - _context.WriteNewLine(); + context.WriteNewLine(); } return targetMethodVar; @@ -249,14 +283,14 @@ public string ResolveField(IFieldSymbol field) found.ThrowIfVariableIsNotValid(); var resolvedField = field.ContainingType.IsGenericType - ? $$"""new FieldReference({{found.VariableName}}.Name, {{found.VariableName}}.FieldType, {{context.TypeResolver.ResolveAny(field.ContainingType, ResolveTargetKind.TypeReference)}})""" + ? $$"""new FieldReference({{found.VariableName}}.Name, {{found.VariableName}}.FieldType, {{context.TypeResolver.Resolve(field.ContainingType, ResolveTargetKind.TypeReference)}})""" : found.VariableName; return resolvedField; } var declaringTypeName = field.ContainingType.FullyQualifiedName(); - return Utils.ImportFromMainModule($"TypeHelpers.ResolveField(\"{declaringTypeName}\",\"{field.Name}\")"); + return ImportReference($"TypeHelpers.ResolveField(\"{declaringTypeName}\",\"{field.Name}\")"); } public string ResolveEventField(IEventSymbol eventSymbol) @@ -268,9 +302,39 @@ public string ResolveEventField(IEventSymbol eventSymbol) found.ThrowIfVariableIsNotValid(); var resolvedField = eventSymbol.ContainingType.IsGenericType - ? $$"""new FieldReference({{found.VariableName}}.Name, {{found.VariableName}}.FieldType, {{context.TypeResolver.ResolveAny(eventSymbol.ContainingType, ResolveTargetKind.TypeReference)}})""" + ? $$"""new FieldReference({{found.VariableName}}.Name, {{found.VariableName}}.FieldType, {{context.TypeResolver.Resolve(eventSymbol.ContainingType, ResolveTargetKind.TypeReference)}})""" : found.VariableName; return resolvedField; } + + public string ImportReference(string expression) => $"assembly.MainModule.ImportReference({expression})"; + + public string MakeGeneticInstanceMethod(string methodReferenceVariable, string methodName, IReadOnlyList resolvedTypeArguments) + { + var hash = new HashCode(); + hash.Add(methodReferenceVariable); + hash.Add(resolvedTypeArguments.Count); + foreach (var t in resolvedTypeArguments) + hash.Add(t); + + var varName = context.Services.Get>().GetOrCreate(hash.ToHashCode(), (context, methodName, resolvedTypeArguments, methodReferenceVariable), + static (hashCode, state) => + { + var genericInstanceVarName = state.context.Naming.SyntheticVariable(state.methodName, ElementKind.GenericInstance); + + state.context.Generate($"var {genericInstanceVarName} = new GenericInstanceMethod({state.methodReferenceVariable});"); + state.context.WriteNewLine(); + foreach (var t in state.resolvedTypeArguments) + { + state.context.Generate($"{genericInstanceVarName}.GenericArguments.Add({t});"); + state.context.WriteNewLine(); + } + return genericInstanceVarName; + }); + + return varName; + } + + public string MapSpecificAttributes(IMethodSymbol method) => string.Empty; // No-op on Mono.Cecil } diff --git a/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilTypeResolver.cs b/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilTypeResolver.cs index 7f588b79..d3c4de40 100644 --- a/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilTypeResolver.cs +++ b/Cecilifier.ApiDriver.MonoCecil/TypeSystem/MonoCecilTypeResolver.cs @@ -9,10 +9,38 @@ namespace Cecilifier.ApiDriver.MonoCecil.TypeSystem; public class MonoCecilTypeResolver(MonoCecilContext context) : TypeResolverBase(context) { - public override ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext) + protected override ResolvedType ResolveNestedTypeCore(INamedTypeSymbol type, in TypeResolutionContext resolutionContext) + { + // collects the type arguments for all types in the parent chain. + var typeArguments = type.GetAllTypeArguments().ToArray(); + return new ResolvedType($"""TypeHelpers.NewRawNestedTypeReference("{type.Name}", module: assembly.MainModule, {Resolve(type.ContainingType.OriginalDefinition, in resolutionContext)}, isValueType: {type.IsValueType.ToKeyword()}, {typeArguments.Length})"""); + } + + protected override ResolvedType ResolveTypeParameter(ITypeSymbol type, in TypeResolutionContext resolutionContext) + { + if (type is not ITypeParameterSymbol typeParameterSymbol) + return null; + + if (resolutionContext.TypeParameterProviderVar == null) + return null; + + var resolvedType = typeParameterSymbol.ContainingSymbol.Kind switch + { + SymbolKind.NamedType => $"(({resolutionContext.TypeParameterProviderVar} is MethodReference methodReference) ? ((GenericInstanceType) methodReference.DeclaringType).ElementType : (IGenericParameterProvider) {resolutionContext.TypeParameterProviderVar} ).GenericParameters[{typeParameterSymbol.Ordinal}]", + SymbolKind.Method => $"{resolutionContext.TypeParameterProviderVar}.GenericParameters[{typeParameterSymbol.Ordinal}]", + _ => null + }; + + return new ResolvedType(resolvedType); + } + + // Mono.Cecil represents Generic Type Parameters as instances of a type, so this method is a no-op in that Api Driver. + public override ResolvedType ResolveTypeParameter(ResolvedType genericTypeParameter, TypeParameterKind typeParameterKind, in TypeResolutionContext resolutionContext) => genericTypeParameter; + + protected override ResolvedType ResolveFromAssembly(ITypeSymbol type, in TypeResolutionContext resolutionContext) { if (type.ContainingType != null) - return Utils.ImportFromMainModule($"typeof({$"""{type.ToDisplayString()}"""})"); + return ImportReference($"typeof({$"""{type.ToDisplayString()}"""})"); var formatOptions = SymbolDisplayFormat.FullyQualifiedFormat .RemoveGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters) @@ -27,27 +55,37 @@ public override ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext nameToResolve = $"{nameToResolve}<{commas}>"; } - return Utils.ImportFromMainModule($"typeof({$"""{nameToResolve}"""})"); + return ImportReference($"typeof({$"""{nameToResolve}"""})"); } public override ResolvedType ResolvePredefinedType(ITypeSymbol type, in TypeResolutionContext resolutionContext) => $"assembly.MainModule.TypeSystem.{type.Name}"; - public override ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResolutionContext resolutionContext) => ResolveAny(elementType, in resolutionContext) + ".MakeArrayType()"; - protected override ResolvedType MakePointerType(ITypeSymbol pointerType, in TypeResolutionContext resolutionContext) => ResolveAny(pointerType, in resolutionContext) + ".MakePointerType()"; + public override ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResolutionContext resolutionContext) => Resolve(elementType, in resolutionContext) + ".MakeArrayType()"; + protected override ResolvedType MakePointerType(ITypeSymbol pointerType, in TypeResolutionContext resolutionContext) => Resolve(pointerType, in resolutionContext) + ".MakePointerType()"; protected override ResolvedType MakeFunctionPointerType(IFunctionPointerTypeSymbol functionPointer, in TypeResolutionContext resolutionContext) { return CecilDefinitionsFactory.FunctionPointerType(this, functionPointer); } - public override ResolvedType MakeGenericInstanceType(ResolvedType typeReference, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext) + public override ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext) { Buffer256 g = new(); var resolutionContextTypeParameterProviderVar = resolutionContext.TypeParameterProviderVar; var typeArgs = CollectTypeArguments(genericTypeSymbol, ref g) .ToImmutableArray() - .Select(t => _context.TypeResolver.ResolveAny(t, ResolveTargetKind.TypeReference.ToTypeResolutionContext(resolutionContextTypeParameterProviderVar))) + .Select(t => _context.TypeResolver.Resolve(t, ResolveTargetKind.TypeReference.ToTypeResolutionContext(resolutionContextTypeParameterProviderVar))) .ToImmutableArray(); - return typeArgs.Length > 0 ? typeReference.MakeGenericInstanceType(typeArgs) : typeReference; + return typeArgs.Length > 0 ? openGenericType.MakeGenericInstanceType(typeArgs) : openGenericType; } + + + public override ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, Span typeArguments, in TypeResolutionContext resolutionContext) + { + return typeArguments.Length > 0 ? openGenericType.MakeGenericInstanceType(typeArguments.ToArray()) : openGenericType; + } + + public override ResolvedType MakeByRefType(in ResolvedType resolvedType) => $"{resolvedType}.MakeByReferenceType()"; + + internal ResolvedType ImportReference(ResolvedType typeReference) => $"assembly.MainModule.ImportReference({typeReference})"; } diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/Cecilifier.ApiDriver.SystemReflectionMetadata.csproj b/Cecilifier.ApiDriver.SystemReflectionMetadata/Cecilifier.ApiDriver.SystemReflectionMetadata.csproj index b545c6b1..cf4e9c4e 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/Cecilifier.ApiDriver.SystemReflectionMetadata.csproj +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/Cecilifier.ApiDriver.SystemReflectionMetadata.csproj @@ -1,7 +1,8 @@  - + + + - net9.0 enable enable diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/DelayedDefinitionsManager.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/DelayedDefinitionsManager.cs index ca09a031..389505a2 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/DelayedDefinitionsManager.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/DelayedDefinitionsManager.cs @@ -4,7 +4,6 @@ using Cecilifier.Core.AST; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Cecilifier.ApiDriver.SystemReflectionMetadata.DelayedDefinitions; @@ -26,8 +25,6 @@ public class DelayedDefinitionsManager private readonly Dictionary _postponedTypeDefinitions = new(); private readonly List _typeDefinitionOrder = new(); - private MethodDefinitionRecord _currentMethod; - internal void RegisterTypeDefinition(string typeVarName, DelayedTypeDefinitionAction action) { _postponedTypeDefinitions.Add(typeVarName, new TypeDefinitionRecord(typeVarName) @@ -39,12 +36,12 @@ internal void RegisterTypeDefinition(string typeVarName, DelayedTypeDefinitionAc _typeDefinitionOrder.Add(typeVarName); } - internal void RegisterMethodDefinition(string declaringTypeVarName, Func newMethodFunc) + internal void RegisterMethodDefinition(string declaringTypeVarName, string methodVar, Func newMethodFunc) { ref var declaringTypeRecord = ref CollectionsMarshal.GetValueRefOrNullRef(_postponedTypeDefinitions, declaringTypeVarName); Debug.Assert(!Unsafe.IsNullRef(ref declaringTypeRecord)); - declaringTypeRecord.Methods.Add(_currentMethod = new MethodDefinitionRecord(newMethodFunc, declaringTypeVarName)); + declaringTypeRecord.Methods.Add(new MethodDefinitionRecord(newMethodFunc, methodVar)); } internal void RegisterFieldDefinition(string declaringTypeVarName, in FieldDefinitionRecord field) @@ -55,19 +52,26 @@ internal void RegisterFieldDefinition(string declaringTypeVarName, in FieldDefin typeRecordOrNullRef.Fields.Add(field); } - public int RegisterLocalVariable(string localVarName, ResolvedType resolvedVarType, Action action) + public int RegisterLocalVariable(string declaringMethodVariable, string localVarName, ResolvedType resolvedVarType, Action action) { - Debug.Assert(_currentMethod != null); var localVariable = new LocalVariableRecord(localVarName, resolvedVarType, action); - _currentMethod.LocalVariables.Add(localVariable); - return _currentMethod.LocalVariables.Count - 1; + var method = _postponedTypeDefinitions.Values.SelectMany(ptd => ptd.Methods).Single(pmd => pmd.MethodVariable == declaringMethodVariable); + Debug.Assert(method != null, $"Method with variable name {declaringMethodVariable} was not found."); + + method.LocalVariables.Add(localVariable); + return method.LocalVariables.Count - 1; } public void RegisterProperty(string propertyName,string propertyDefinitionVariable, string declaringTypeName, string declaringTypeVariable, Action propertyProcessor) { GetCurrentTypeDefinition().Properties.Add(new PropertyDefinitionRecord(propertyName, propertyDefinitionVariable, declaringTypeName, propertyProcessor)); } + + public void RegisterEvent(string eventName,string propertyDefinitionVariable, string declaringTypeName, Action eventProcessor) + { + GetCurrentTypeDefinition().Events.Add(new EventDefinitionRecord(eventName, propertyDefinitionVariable, declaringTypeName, eventProcessor)); + } /// /// This method must be invoked after the member has been registered with the DelayedDefinitionsManager, in most cases diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/EventDefinitionRecord.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/EventDefinitionRecord.cs new file mode 100644 index 00000000..91319622 --- /dev/null +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/EventDefinitionRecord.cs @@ -0,0 +1,9 @@ +using Cecilifier.Core.AST; + +namespace Cecilifier.ApiDriver.SystemReflectionMetadata.DelayedDefinitions; + +public record struct EventDefinitionRecord(string Name, string DefinitionVariable, string DeclaringTypeName, Action Processor) +{ + public bool IsValid => Name != null; +} + diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/MethodDefinitionRecord.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/MethodDefinitionRecord.cs index ef615317..c617bb02 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/MethodDefinitionRecord.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/MethodDefinitionRecord.cs @@ -1,6 +1,6 @@ namespace Cecilifier.ApiDriver.SystemReflectionMetadata.DelayedDefinitions; -internal record MethodDefinitionRecord(Func DefinitionFunction, string DeclaringTypeVarName) +internal record MethodDefinitionRecord(Func DefinitionFunction, string MethodVariable) { public string FirstParameterHandle => "MetadataTokens.ParameterHandle(metadata.GetRowCount(TableIndex.Param) + 1)"; public string LocalSignatureHandleVariable { get; set; } = "default(StandaloneSignatureHandle)"; diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/TypeDefinitionRecord.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/TypeDefinitionRecord.cs index e28c957d..00361859 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/TypeDefinitionRecord.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/DelayedDefinitions/TypeDefinitionRecord.cs @@ -14,7 +14,7 @@ internal record struct TypeDefinitionRecord(string TypeReferenceVariable) /// /// Name of the variable that stores the type definition emitted to represent a type definition. This is only valid after the type has been processed. See /// - public string TypeDefinitionVariable { get; internal set; } + public string? TypeDefinitionVariable { get; internal set; } /// /// The name of the variable representing the definition of the first method of the type. @@ -23,6 +23,8 @@ internal record struct TypeDefinitionRecord(string TypeReferenceVariable) public IList Properties { get; } = new List(); + public IList Events { get; } = new List(); + internal IList Fields { get; } = new List(); public required DelayedTypeDefinitionAction DefinitionFunction { internal get; init; } diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/DllImportProcessor.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/DllImportProcessor.cs new file mode 100644 index 00000000..afb4a2a4 --- /dev/null +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/DllImportProcessor.cs @@ -0,0 +1,102 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using Cecilifier.Core.Extensions; +using CustomAttributeArgument = Cecilifier.Core.ApiDriver.Attributes.CustomAttributeArgument; +using CustomAttributeNamedArgument = Cecilifier.Core.ApiDriver.Attributes.CustomAttributeNamedArgument; + +namespace Cecilifier.ApiDriver.SystemReflectionMetadata; + +public class DllImportProcessor +{ + // Returns the entry point method name in quotes. + public static string EntryPoint(ReadOnlySpan customAttributeArguments, string originalMethodName) => $""" + "{AttributePropertyOrDefaultValue(customAttributeArguments, "EntryPoint",$"\"{originalMethodName}\"") }" + """; + // For more information and default values see + // https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute + public static string MethodImportAttributesFrom(ReadOnlySpan customAttributeArguments) + { + return CallingConventionFrom(customAttributeArguments) + .AppendEnumFlag(CharSetFrom(customAttributeArguments)) + .AppendEnumFlag(SetLastErrorFrom(customAttributeArguments)) + .AppendEnumFlag(ExactSpellingFrom(customAttributeArguments)) + .AppendEnumFlag(BestFitMappingFrom(customAttributeArguments)) + .AppendEnumFlag(ThrowOnUnmappableCharFrom(customAttributeArguments)) + .ToString(); + } + + static StringBuilder CallingConventionFrom(ReadOnlySpan customAttributeArguments) + { + var callConventionSpan = AttributePropertyOrDefaultValue(customAttributeArguments, "CallingConvention", "Winapi").AsSpan(); + + // ensures we use the enum member simple name; Parse() fails if we pass a qualified enum member + var index = callConventionSpan.LastIndexOf('.'); + callConventionSpan = callConventionSpan.Slice(index + 1); + + return new StringBuilder(CallingConventionToCecil(Enum.Parse(callConventionSpan))); + } + + static string CharSetFrom(ReadOnlySpan customAttributeArguments) + { + var enumMemberName = AttributePropertyOrDefaultValue(customAttributeArguments, "CharSet", "None").AsSpan(); + + // Only use the actual enum member name Parse() fails if we pass a qualified enum member + var index = enumMemberName.LastIndexOf('.'); + var charSet = Enum.Parse(enumMemberName.Slice(index + 1)); + return charSet == CharSet.None ? string.Empty : $"{AttributeName}.CharSet{charSet}"; + } + + static string SetLastErrorFrom(ReadOnlySpan customAttributeArguments) + { + var setLastError = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "SetLastError", "false")); + return setLastError ? $"{AttributeName}.SetLastError" : string.Empty; + } + + static string ExactSpellingFrom(ReadOnlySpan customAttributeArguments) + { + var exactSpelling = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "ExactSpelling", "false")); + return exactSpelling ? $"{AttributeName}.ExactSpelling" : string.Empty; + } + + static string BestFitMappingFrom(ReadOnlySpan customAttributeArguments) + { + var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "BestFitMapping", "true")) + ? "BestFitMappingEnable" + : "BestFitMappingDisable"; + + return $"{AttributeName}.{bestFitMapping}"; + } + + static string ThrowOnUnmappableCharFrom(ReadOnlySpan customAttributeArguments) + { + var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(customAttributeArguments, "ThrowOnUnmappableChar", "false")) + ? "ThrowOnUnmappableCharEnable" + : "ThrowOnUnmappableCharDisable"; + + return $"{AttributeName}.{bestFitMapping}"; + } + + static string AttributePropertyOrDefaultValue(ReadOnlySpan customAttributeArguments, string propertyName, string defaultValue) + { + return customAttributeArguments.ToArray().OfType().FirstOrDefault(arg => arg.Name == propertyName)?.Value?.ToString() ?? defaultValue; + } + + static string CallingConventionToCecil(CallingConvention callingConvention) + { + var callingConventionSuffix = callingConvention switch + { + CallingConvention.Cdecl => "CDecl", + CallingConvention.Winapi => "WinApi", + CallingConvention.FastCall => "FastCall", + CallingConvention.StdCall => "StdCall", + CallingConvention.ThisCall => "ThisCall", + + _ => throw new Exception($"Unexpected calling convention: {callingConvention}") + }; + + return $"{AttributeName}.CallingConvention{callingConventionSuffix}"; + } + + private static string AttributeName = nameof(MethodImportAttributes); +} diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataDefinitionsFactory.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataDefinitionsFactory.cs index d3dc4782..e6634c8c 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataDefinitionsFactory.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataDefinitionsFactory.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Cecilifier.ApiDriver.SystemReflectionMetadata.CustomAttributes; using Cecilifier.ApiDriver.SystemReflectionMetadata.DelayedDefinitions; @@ -17,6 +19,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using CustomAttributeNamedArgument = Cecilifier.Core.ApiDriver.Attributes.CustomAttributeNamedArgument; namespace Cecilifier.ApiDriver.SystemReflectionMetadata; @@ -45,10 +48,9 @@ public IEnumerable Type( var {typeVar} = metadata.AddTypeReference({resolutionScope}, metadata.GetOrAddString("{typeNamespace}"), metadata.GetOrAddString("{fixedTypeName}")); """); - ProcessGenericTypeParameters(context, typeParameters); + DefineGenericTypeParametersVariables(context, typeParameters); - // We need to pass the handle of the 1st field/method defined in the module so we need to postpone the type generation after we have visited - // all types/members. + // We need to pass the handle of the 1st field/method defined in the module so we need to postpone the type generation after we have visited all types/members. TypedContext(context).DelayedDefinitionsManager.RegisterTypeDefinition(typeVar, DefineDelayed); void DefineDelayed(SystemReflectionMetadataContext ctx, ref TypeDefinitionRecord typeRecord) { @@ -67,7 +69,7 @@ void DefineDelayed(SystemReflectionMetadataContext ctx, ref TypeDefinitionRecord {attrs}, metadata.GetOrAddString("{typeNamespace}"), metadata.GetOrAddString("{fixedTypeName}"), - { (baseType == null ? "default" : context.TypeResolver.ResolveAny(baseType!, ResolveTargetKind.TypeReference)) }, + { (baseType == null ? "default" : context.TypeResolver.Resolve(baseType!, ResolveTargetKind.TypeReference)) }, fieldList: {firstFieldHandle ?? ApiDriverConstants.FieldDefinitionTableNextAvailableEntry}, methodList: {typeRecord.FirstMethodHandle ?? ApiDriverConstants.MethodDefinitionTableNextAvailableEntry}); """)); @@ -92,6 +94,19 @@ void DefineDelayed(SystemReflectionMetadataContext ctx, ref TypeDefinitionRecord ctx.WriteNewLine(); } + foreach (var eventRecord in typeRecord.Events) + { + // process each event passing the type definition variable + eventRecord.Processor(ctx, eventRecord.Name, eventRecord.DefinitionVariable, eventRecord.DeclaringTypeName); + } + + var firstEvent = typeRecord.Events.FirstOrDefault(); + if (firstEvent.IsValid) + { + ctx.Generate($"metadata.AddEventMap({typeRecord.TypeDefinitionVariable}, {firstEvent.DefinitionVariable});"); + ctx.WriteNewLine(); + } + if (definitionContext.ParentDefinitionVariable != null) { var parentTypeDefinitionVariable = ctx.DelayedDefinitionsManager.GetTypeDefinitionVariableFromTypeReferenceVariable(definitionContext.ParentDefinitionVariable); @@ -110,31 +125,14 @@ void DefineDelayed(SystemReflectionMetadataContext ctx, ref TypeDefinitionRecord foreach(var itf in interfaces) { - context.Generate($"metadata.AddInterfaceImplementation({typeRecord.TypeDefinitionVariable}, {context.TypeResolver.ResolveAny(itf, ResolveTargetKind.TypeReference)});"); + context.Generate($"metadata.AddInterfaceImplementation({typeRecord.TypeDefinitionVariable}, {context.TypeResolver.Resolve(itf, ResolveTargetKind.TypeReference)});"); context.WriteNewLine(); } - var index = 0; - foreach (var genericTypeParameter in outerTypeParameters.Concat(typeParameters)) - { - ctx.Generate($"""metadata.AddGenericParameter({typeRecord.TypeDefinitionVariable}, GenericParameterAttributes.None, metadata.GetOrAddString("{genericTypeParameter.Identifier.Text}"), {index++});"""); - ctx.WriteNewLine(); - } + AddTypeParameters(ctx, outerTypeParameters.Concat(typeParameters), typeRecord.TypeDefinitionVariable); + ctx.WriteNewLine(); } - - static void ProcessGenericTypeParameters(IVisitorContext context, IList typeParamList) - { - for (int i = 0; i < typeParamList.Count; i++) - { - var symbol = context.SemanticModel.GetDeclaredSymbol(typeParamList[i]).EnsureNotNull(); - var genericParamName = typeParamList[i].Identifier.Text; - var parentName = symbol.TypeParameterKind == TypeParameterKind.Method ? symbol.DeclaringMethod?.OriginalDefinition.ToDisplayString() : symbol.DeclaringType?.OriginalDefinition.ToDisplayString(); - - // register a variable representing the type parameter; uses its index as its name since in SRM the type parameter is represented by its index. - context.DefinitionVariables.RegisterNonMethod(parentName, genericParamName, VariableMemberKind.TypeParameter, i.ToString()); - } - } } public void UpdateBaseTypeIfNeeded(IVisitorContext context, ITypeSymbol typeSymbol, string typeDefinitionVariable) @@ -144,6 +142,8 @@ public void UpdateBaseTypeIfNeeded(IVisitorContext context, ITypeSymbol typeSymb public IEnumerable Method(IVisitorContext context, IMethodSymbol methodSymbol, BodiedMemberDefinitionContext bodiedMemberDefinitionContext, string methodName, string methodModifiers, IList typeParameters) { + DefineGenericTypeParametersVariables(context, typeParameters); + // Resolve the method to make sure there's a method ref available (this will be used to fulfill any references to this method) context.MemberResolver.ResolveMethod(methodSymbol); @@ -158,7 +158,7 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS } var memberParentDefinitionVariable = bodiedMemberDefinitionContext.Member.ParentDefinitionVariable ?? throw new ArgumentNullException(nameof(bodiedMemberDefinitionContext.Member.ParentDefinitionVariable)); - TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(memberParentDefinitionVariable, (ctx, methodRecord) => + TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(memberParentDefinitionVariable,bodiedMemberDefinitionContext.Member.DefinitionVariable, (ctx, methodRecord) => { EmitLocalVariables(ctx, bodiedMemberDefinitionContext.Member.Identifier, in methodRecord); @@ -166,15 +166,16 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS Debug.Assert(methodSignatureVar.IsValid); var methodDefVar = bodiedMemberDefinitionContext.IlContext!.AssociatedMethodVariable; - var bodyOffset = methodSymbol.ContainingType.TypeKind == TypeKind.Interface + var bodyOffset = methodSymbol.ContainingType.TypeKind == TypeKind.Interface || methodSymbol.IsExtern || methodSymbol.IsAbstract ? "-1" : $"methodBodyStream.AddMethodBody({bodiedMemberDefinitionContext.IlContext.VariableName}, localVariablesSignature: {methodRecord.LocalSignatureHandleVariable})"; var firstParameterHandle = AddParametersMetadata(ctx, methodSymbol.Parameters.Select(p => p.Name)); + var methodImplAttributes = "MethodImplAttributes.IL | MethodImplAttributes.Managed".AppendEnumFlag(GetMethodImplementationFlagsStringFrom(methodSymbol.MethodImplementationFlags)); ctx.Generate($""" var {methodDefVar} = metadata.AddMethodDefinition( {methodModifiers}, - MethodImplAttributes.IL | MethodImplAttributes.Managed, + {methodImplAttributes}, metadata.GetOrAddString("{methodName}"), {methodSignatureVar.VariableName}, {bodyOffset}, @@ -182,6 +183,8 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS """); ctx.WriteNewLine(); ctx.WriteNewLine(); + + AddTypeParameters(ctx, typeParameters, methodDefVar); ctx.DefinitionVariables.ExecuteDependentRegistrations(methodDefVar); @@ -189,6 +192,23 @@ public IEnumerable Method(IVisitorContext context, IMethodSymbol methodS }); yield break; + + static string GetMethodImplementationFlagsStringFrom(MethodImplAttributes methodImplAttributes) + { + StringBuilder result = new(); + const string enumName = nameof(MethodImplAttributes); + // Ignore 0, since both MethodImplAttributes.IL and MethodImplAttributes.Managed have this value and both + // are mapped to IL name :( (we hard code these two flags in the code - see the caller of this method) + foreach (var enumMember in Enum.GetValues().Where(em => em != 0)) + { + if (methodImplAttributes.HasFlag(enumMember)) + { + result.AppendEnumFlag($"{enumName}.{enumMember}"); + } + } + + return result.ToString(); + } } public IEnumerable Method(IVisitorContext context, @@ -200,13 +220,24 @@ public IEnumerable Method(IVisitorContext context, Func returnTypeResolver, out MethodDefinitionVariable methodDefinitionVariable) { + // Some parts of the code assumes that once this method returns the variable specified in 'definitionContext.Member.DefinitionVariable` + // have been defined. + // Since in SRM the definition of that variable will be postponed to the end of the processing (see 'DelayedDefinitionManager') when this + // method returns a variable to store a 'method reference' have been registered instead so we check if that 'method reference' has already + // been registered and do not schedule another registration/processing for the method. + methodDefinitionVariable = ((SystemReflectionMetadataMemberResolver) context.MemberResolver).LookupRegisteredMethod(declaringTypeName, definitionContext.Member.Name, parameters, typeParameters.AsReadOnly()); + if (methodDefinitionVariable.IsValid) + return Array.Empty(); + + DefineGenericTypeParametersVariables(context, definitionContext.Member.Identifier, typeParameters); + var methodRefVar = context.MemberResolver.ResolveMethod( - declaringTypeName, - definitionContext.Member.ParentDefinitionVariable, - definitionContext.Member.Identifier, - returnTypeResolver(context), - parameters, - typeParameters.Count, + declaringTypeName, + definitionContext.Member.ParentDefinitionVariable, + definitionContext.Member.Name, + returnTypeResolver(context), + parameters, + typeParameters.ToArray(), definitionContext.Options); methodDefinitionVariable = context.DefinitionVariables.FindByVariableName(methodRefVar) ?? MethodDefinitionVariable.MethodNotFound; @@ -214,22 +245,23 @@ public IEnumerable Method(IVisitorContext context, // register all parameters so we can reference them when emitting the method body for (int i = 0; i < parameters.Count; i++) { - context.DefinitionVariables.RegisterNonMethod(definitionContext.Member.ContainingTypeName, parameters[i].Name, VariableMemberKind.Parameter, $"{i + 1}"); + context.DefinitionVariables.RegisterNonMethod(definitionContext.Member.Identifier, parameters[i].Name, VariableMemberKind.Parameter, $"{i + 1}"); } - TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(definitionContext.Member.ParentDefinitionVariable, (ctx, methodRecord) => + Debug.Assert(definitionContext.Member.ParentDefinitionVariable != null); + TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(definitionContext.Member.ParentDefinitionVariable, definitionContext.Member.DefinitionVariable, (ctx, methodRecord) => { EmitLocalVariables(ctx, definitionContext.Member.Identifier, in methodRecord); var methodReferenceToFind = new MethodDefinitionVariable( VariableMemberKind.MethodSignature, declaringTypeName, - definitionContext.Member.Identifier, + definitionContext.Member.Name, parameters.Select(p => p.ElementType.Expression).ToArray(), - typeParameters.Count); + typeParameters.ToArray()); var methodSignatureVar = ctx.DefinitionVariables.GetMethodVariable(methodReferenceToFind); - Debug.Assert(methodSignatureVar.IsValid); + methodSignatureVar.ThrowIfVariableIsNotValid($"Method={declaringTypeName}.{definitionContext.Member.Name}"); var methodDefVar = definitionContext.Member.DefinitionVariable; var firstParameterHandle = AddParametersMetadata(ctx, parameters.Select(p => p.Name)); @@ -255,10 +287,13 @@ public IEnumerable Method(IVisitorContext context, declaringTypeName, definitionContext.Member.Name, parameters.Select(p => p.ElementType.Expression).ToArray(), - 0, + [], //TODO: This seems wrong. If the method is generic shouldn't we pass the generic type parameters? methodDefVar); ctx.DefinitionVariables.RegisterMethod(toBeRegistered); + + AddTypeParameters(ctx, typeParameters, methodDefVar); + ctx.DefinitionVariables.ExecuteDependentRegistrations(methodDefVar); return methodDefVar; @@ -280,10 +315,11 @@ public IEnumerable Constructor(IVisitorContext context, BodiedMemberDefi """); var parentDefinitionVariable = definitionContext.Member.ParentDefinitionVariable ?? throw new ArgumentNullException(nameof(definitionContext.Member.ParentDefinitionVariable)); - TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(parentDefinitionVariable, (ctx, methodRecord) => + TypedContext(context).DelayedDefinitionsManager.RegisterMethodDefinition(parentDefinitionVariable, definitionContext.Member.DefinitionVariable, (ctx, methodRecord) => { EmitLocalVariables(ctx, "ctor", in methodRecord); + Debug.Assert(definitionContext.IlContext != null); var ctorDefVar = ctx.Naming.SyntheticVariable($"{nameAsIdentifier}_Ctor", ElementKind.MemberReference); ctx.Generate($""" var {ctorDefVar} = metadata.AddMethodDefinition( @@ -303,7 +339,7 @@ public IEnumerable Constructor(IVisitorContext context, BodiedMemberDefi public IEnumerable Field(IVisitorContext context, in MemberDefinitionContext definitionContext, ISymbol fieldOrEvent, ITypeSymbol fieldType, string fieldAttributes, bool isVolatile, bool isByRef, in FieldInitializationData initializer = default) { - var resolvedType = context.TypeResolver.ResolveAny(fieldType, new TypeResolutionContext(ResolveTargetKind.Field, fieldType.ElementTypeSymbolOf().IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); + var resolvedType = context.TypeResolver.Resolve(fieldType, new TypeResolutionContext(ResolveTargetKind.Field, fieldType.ElementTypeSymbolOf().IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); return Field(context, definitionContext, fieldOrEvent.ContainingType.ToDisplayString(), resolvedType, fieldAttributes, isVolatile, isByRef, initializer); } @@ -390,13 +426,38 @@ public IEnumerable FieldReference(IVisitorContext context, string fieldR return span.Slice(0, count + 1).ToArray(); } - public IEnumerable MethodBody(IVisitorContext context, string methodName, IlContext ilContext, ResolvedType[] localVariableTypes, InstructionRepresentation[] instructions) => []; + public IEnumerable MethodBody(IVisitorContext context, string methodName, IlContext ilContext, ResolvedType[] localVariableTypes, InstructionRepresentation[] instructions) + { + var exps = new List(instructions.Length); + + var tagToLabelVariable = new Dictionary(); + // emit an instruction for each instruction that has a 'Tag' + foreach (var instruction in instructions.Where(inst => !inst.Ignore)) + { + if (instruction.Tag != null) + { + var labelVariable = context.Naming.SyntheticVariable(instruction.Tag, ElementKind.Label); + + exps.Add(context.ApiDriver.EmitDefineLabel(context, ilContext, labelVariable)); + exps.Add(context.ApiDriver.EmitMarkLabel(context, ilContext, labelVariable)); + + tagToLabelVariable[instruction.Tag] = labelVariable; + } + + exps.Add(instruction.BranchTargetTag != null + ? context.ApiDriver.EmitCilBranchInstruction(context, ilContext, instruction.OpCode, tagToLabelVariable[instruction.BranchTargetTag]) + : context.ApiDriver.EmitCilInstruction(context, ilContext, instruction.OpCode, instruction.Operand)); + } + + return exps.ToArray(); + } public DefinitionVariable LocalVariable(IVisitorContext context, string variableName, string methodDefinitionVariableName, ResolvedType resolvedVarType) { - var variableIndex = TypedContext(context).DelayedDefinitionsManager.RegisterLocalVariable(variableName, resolvedVarType, (ctx, localVariableEncoderVar, localVarType) => + var variableIndex = TypedContext(context).DelayedDefinitionsManager.RegisterLocalVariable(methodDefinitionVariableName, variableName, resolvedVarType, (ctx, localVariableEncoderVar, localVarType) => { context.Generate($"{localVariableEncoderVar}.AddVariable().{localVarType};"); + context.WriteNewLine(); }); // This is a hack. SRM accesses local variables by index, and Cecilifier does not have a way to pass that index around; it only has variable names, @@ -407,9 +468,7 @@ public DefinitionVariable LocalVariable(IVisitorContext context, string variable public IEnumerable Property(IVisitorContext context, BodiedMemberDefinitionContext definitionContext, string declaringTypeName, List propertyParameters, ResolvedType propertyType) { - var propertySignatureTempVar = context.Naming - .With(NamingOptions.NoCasingElementNames) - .Without(NamingOptions.CamelCaseElementNames).SyntheticVariable($"{definitionContext.Member.Name.CamelCase()}_blobBuilder", ElementKind.MemberReference); + var propertySignatureTempVar = context.Naming.SyntheticVariable($"{definitionContext.Member.Name}BlobBuilder", ElementKind.None); TypedContext(context).DelayedDefinitionsManager.RegisterProperty(definitionContext.Member.Name, definitionContext.Member.DefinitionVariable, declaringTypeName, definitionContext.Member.ParentDefinitionVariable!, static (context, propertyName, propertyDefinitionVariable, declaringTypeName, declaringTypeVariable) => @@ -448,6 +507,35 @@ public IEnumerable Property(IVisitorContext context, BodiedMemberDefinit """)]; } + public IEnumerable Event(IVisitorContext context, BodiedMemberDefinitionContext eventSpec, string declaringTypeName, ResolvedType eventType, string addAccessorVariable, string removeAccessorVariable) + { + TypedContext(context).DelayedDefinitionsManager.RegisterEvent(eventSpec.Member.Name, eventSpec.Member.DefinitionVariable, declaringTypeName, + static (context, eventName, eventDefinitionVariable, declaringTypeName) => + { + var addMethodVariable = context.DefinitionVariables.GetVariable($"add_{eventName}", VariableMemberKind.Method, declaringTypeName); + var removeMethodVariable = context.DefinitionVariables.GetVariable($"remove_{eventName}", VariableMemberKind.Method, declaringTypeName); + + foreach (var accessor in new[] {(addMethodVariable, "Adder"), (removeMethodVariable, "Remover") }) + { + if (!accessor.Item1.IsValid) + continue; + + context.Generate($""" + // Associate method {accessor.Item1.MemberName} with event {eventName} + metadata.AddMethodSemantics( + {eventDefinitionVariable}, + MethodSemanticsAttributes.{accessor.Item2}, + {accessor.Item1.VariableName}); + """); + + context.DefinitionVariables.ExecuteDependentRegistrations(eventDefinitionVariable); + context.WriteNewLine(); + } + }); + + return [Format($$"""var {{eventSpec.Member.DefinitionVariable}} = metadata.AddEvent(EventAttributes.None, metadata.GetOrAddString("{{eventSpec.Member.Name}}"), {{eventType}});""")]; + } + public IEnumerable Attribute(IVisitorContext context, IMethodSymbol attributeCtor, string attributeVarBaseName, string attributeTargetVar, VariableMemberKind targetKind, params CustomAttributeArgument[] arguments) { var attributeEncoderVariable = context.DefinitionVariables.GetVariable("EncoderMetaName", VariableMemberKind.None, attributeVarBaseName); @@ -482,6 +570,7 @@ public IEnumerable Attribute(IVisitorContext context, IMethodSymbol attr // type definition to be known up-front adding complexity, so in that case we simply delegate to `DelayedDefinitionManager` context.DefinitionVariables.RegisterDependentOnRegistration(attributeTargetVar, context, (ctx, state) => { + Debug.Assert(state != null); var target = (NonTypeAttributeTargetState) state; AddAttributeTo(ctx, target.AttributeTarget, target.ResolvedAttributeCtor, target.AttributeEncoderVariable); }, new NonTypeAttributeTargetState(attributeTargetVar, resolvedAttrCtor, attributeEncoderVariable.VariableName)); @@ -500,6 +589,43 @@ static void AddAttributeTo(IVisitorContext ctx, string targetVariable, string re } } + public void OverrideBaseMethod(IVisitorContext context, string overriderMethodVar, string? overridenMethod) { /*NOOP on SRM */} + + public IEnumerable PInvoke(IVisitorContext context, string moduleName, string methodVar, string methodName, ReadOnlySpan customAttributeArguments) + { + var methodImportAttributes = DllImportProcessor.MethodImportAttributesFrom(customAttributeArguments); + var entryPoint = DllImportProcessor.EntryPoint(customAttributeArguments, methodName); + context.DefinitionVariables.RegisterDependentOnRegistration(methodVar, context, (ctx, _) => + { + // retrieve the variable for the native module, if other pinvokes targetting the module has already been processed. + var moduleVar = GetOrRegisterModuleReference(ctx, moduleName); + moduleVar.ThrowIfVariableIsNotValid($"Unable to find/register variable for module {moduleName}"); + + ctx.Generate($""" + metadata.AddMethodImport( + method: {methodVar}, + attributes: {methodImportAttributes}, + name: metadata.GetOrAddString({entryPoint}), + module: {moduleVar.VariableName}); + """); + }, null); + + return []; + + static DefinitionVariable GetOrRegisterModuleReference(IVisitorContext context, string moduleName) + { + var moduleVar = context.DefinitionVariables.GetVariable($"module-{moduleName}", VariableMemberKind.ModuleReference); + if (moduleVar.IsValid) + return moduleVar; + + var moduleVarName = context.Naming.SyntheticVariable($"{Path.GetFileNameWithoutExtension(moduleName)}ModuleRef", ElementKind.None); + context.Generate($"""var {moduleVarName} = metadata.AddModuleReference(metadata.GetOrAddString("{moduleName}"));"""); + context.WriteNewLine(); + + return context.DefinitionVariables.RegisterNonMethod(string.Empty, moduleName, VariableMemberKind.ModuleReference, moduleVarName); + } + } + private SystemReflectionMetadataContext TypedContext(IVisitorContext context) => ((SystemReflectionMetadataContext) context); static void EmitLocalVariables(SystemReflectionMetadataContext context, string methodName, ref readonly MethodDefinitionRecord methodRecord) @@ -543,6 +669,110 @@ private static string AddParametersMetadata(SystemReflectionMetadataContext ctx, } static string Format(CecilifierInterpolatedStringHandler cecilFormattedString) => StringExtensions.Indented(cecilFormattedString); + + private static void AddTypeParameters(SystemReflectionMetadataContext ctx, IEnumerable typeParameters, string entityHandleVariable) + { + var index = 0; + foreach (var genericTypeParameter in typeParameters) + { + var typeParameterSymbol = ctx.SemanticModel.GetDeclaredSymbol(genericTypeParameter).EnsureNotNull(); + var typeParameterVarName = ctx.Naming.SyntheticVariable(typeParameterSymbol.Name, ElementKind.GenericParameter); + + // We only need to assign the newly constructed generic type parameter if we need to set a `type` constraint + var varAssignment = typeParameterSymbol.ConstraintTypes.Length > 0 || typeParameterSymbol.HasValueTypeConstraint || typeParameterSymbol.HasUnmanagedTypeConstraint + ? $"var {typeParameterVarName} = " + : string.Empty; + + ctx.Generate($"""{varAssignment}metadata.AddGenericParameter({entityHandleVariable}, {GenericParameterAttributesFor(typeParameterSymbol)}, metadata.GetOrAddString("{genericTypeParameter.Identifier.Text}"), {index++});"""); + foreach (var constraint in typeParameterSymbol.ConstraintTypes) + { + ctx.WriteNewLine(); + var resolvedType = ctx.TypeResolver.Resolve(constraint, ResolveTargetKind.GenericTypeParameterConstraint); + ctx.Generate(Format($"metadata.AddGenericParameterConstraint({typeParameterVarName}, {resolvedType});")); + } + + ctx.WriteNewLine(); + + if (typeParameterSymbol.HasUnmanagedTypeConstraint) + { + ctx.Generate(Format($$""" + { + var typeSpecificationSig = new BlobEncoder(new BlobBuilder()).TypeSpecificationSignature(); + typeSpecificationSig.CustomModifiers().AddModifier({{ctx.TypeResolver.Resolve(ctx.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference)}}, isOptional: false); + typeSpecificationSig.Type({{ctx.TypeResolver.Bcl.System.ValueType}}, isValueType: true); + metadata.AddGenericParameterConstraint({{typeParameterVarName}}, metadata.AddTypeSpecification(metadata.GetOrAddBlob(typeSpecificationSig.Builder))); + } + """)); + ctx.WriteNewLine(); + } + else if (typeParameterSymbol.HasValueTypeConstraint) + { + ctx.Generate(Format($"metadata.AddGenericParameterConstraint({typeParameterVarName}, {ctx.TypeResolver.Bcl.System.ValueType});")); + ctx.WriteNewLine(); + } + } + static string GenericParameterAttributesFor(ITypeParameterSymbol typeParameterSymbol) + { + Span span = stackalloc char[1024]; + Span target = span; + if (typeParameterSymbol.HasReferenceTypeConstraint) + target = target.AppendEnumFlag("GenericParameterAttributes.ReferenceTypeConstraint"); + + if (typeParameterSymbol.HasConstructorConstraint || typeParameterSymbol.HasValueTypeConstraint) + target = target.AppendEnumFlag("GenericParameterAttributes.DefaultConstructorConstraint", target.Length == span.Length); + + if (typeParameterSymbol.HasValueTypeConstraint) + target = target.AppendEnumFlag("GenericParameterAttributes.NotNullableValueTypeConstraint", target.Length == span.Length); + + if (typeParameterSymbol.Variance == VarianceKind.In) + target = target.AppendEnumFlag("GenericParameterAttributes.Contravariant", target.Length == span.Length); + + if (typeParameterSymbol.Variance == VarianceKind.Out) + target = target.AppendEnumFlag("GenericParameterAttributes.Covariant", target.Length == span.Length); + + if (span.Length != target.Length) + return span.Slice(0, span.Length - target.Length).ToString(); + + return "GenericParameterAttributes.None"; + } + } + + // Assumes type parameters: + // - Have no constraints (GenericParameterAttributes.None) + // - Have no attributes (GenericParameterAttributes.None) + private static void AddTypeParameters(SystemReflectionMetadataContext ctx, IEnumerable typeParameterNames, string entityHandleVariable) + { + var index = 0; + foreach (var typeParameterName in typeParameterNames) + { + var varAssignment = string.Empty; + + ctx.Generate($"""{varAssignment}metadata.AddGenericParameter({entityHandleVariable}, GenericParameterAttributes.None, metadata.GetOrAddString("{typeParameterName}"), {index++});"""); + ctx.WriteNewLine(); + } + } + + private static void DefineGenericTypeParametersVariables(IVisitorContext context, IList typeParamList) + { + for (int i = 0; i < typeParamList.Count; i++) + { + var symbol = context.SemanticModel.GetDeclaredSymbol(typeParamList[i]).EnsureNotNull(); + var parentName = symbol.TypeParameterKind == TypeParameterKind.Method ? symbol.DeclaringMethod?.OriginalDefinition.ToDisplayString() : symbol.DeclaringType?.OriginalDefinition.ToDisplayString(); + + // register a variable representing the type parameter; uses its index as its name since in SRM the type parameter is represented by its index. + Debug.Assert(parentName != null); + context.DefinitionVariables.RegisterNonMethod(parentName, typeParamList[i].Identifier.Text, VariableMemberKind.TypeParameter, i.ToString()); + } + } + + private static void DefineGenericTypeParametersVariables(IVisitorContext context, string parentName, IList typeParamList) + { + for (int i = 0; i < typeParamList.Count; i++) + { + // register a variable representing the type parameter; uses its index as its name since in SRM the type parameter is represented by its index. + context.DefinitionVariables.RegisterNonMethod(parentName, typeParamList[i], VariableMemberKind.TypeParameter, i.ToString()); + } + } } file record struct NonTypeAttributeTargetState (string AttributeTarget, string ResolvedAttributeCtor, string AttributeEncoderVariable); diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataGeneratorDriver.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataGeneratorDriver.cs index c0be3e55..13886512 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataGeneratorDriver.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/SystemReflectionMetadataGeneratorDriver.cs @@ -1,4 +1,5 @@ -using System.Reflection.Emit; +using System.Diagnostics; +using System.Reflection.Emit; using Cecilifier.Core; using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.DefinitionsFactory; @@ -7,6 +8,7 @@ using Cecilifier.Core.Extensions; using Cecilifier.Core.TypeSystem; using Microsoft.CodeAnalysis; +using OpCode = System.Reflection.Emit.OpCode; namespace Cecilifier.ApiDriver.SystemReflectionMetadata; @@ -120,6 +122,8 @@ internal static class Extensions """; } + public ApiDriverCapabilities DriverCapabilities => ApiDriverCapabilities.None; + public int PreambleLineCount => 74; public IReadOnlyCollection AssemblyReferences { get; } = [typeof(System.Reflection.Metadata.BlobBuilder).Assembly.Location]; @@ -152,17 +156,18 @@ public string EmitCilInstruction(IVisitorContext context, IlContext il, OpCod CilOperandValue { Type.SpecialType: SpecialType.System_Char } operandValue => $"{il.VariableName}.CodeBuilder.WriteInt32({operandValue.Value});", CilOperandValue { Type.TypeKind: TypeKind.Enum } enumValue => $"{il.VariableName}.CodeBuilder.Write{((INamedTypeSymbol) enumValue.Type).EnumUnderlyingType!.Name}({(int)enumValue.Value});", CilOperandValue operandValue => $"{il.VariableName}.CodeBuilder.Write{operandValue.Type.Name}({operandValue.Value});", - CilLocalVariableHandle localVariableHandle => $"{il.VariableName}.CodeBuilder.WriteInt32({localVariableHandle.Value});", - - //TODO: Fix name of WriteX() method to be called; it is not always derivable from the type + CilLocalVariableHandle localVariableHandle => $"{il.VariableName}.CodeBuilder.{WriteMethodFor(opCode)}({localVariableHandle.Value});", _ => $"{il.VariableName}.CodeBuilder.Write{operand.GetType().Name}({operand});" } }} """"; + + static string WriteMethodFor(OpCode opCode) => opCode.Size == 1 ? "WriteByte" : "WriteInt16"; } public void WriteCilInstruction(IVisitorContext context, IlContext il, OpCode opCode, T? operand, string? comment = null) { + Debug.Assert(opCode.FlowControl != FlowControl.Branch); context.Generate($"{EmitCilInstruction(context, il, opCode, operand, comment)}"); // Use interpolated string to force usage of CecilifierInterpolatedStringHandler context.WriteNewLine(); } @@ -174,28 +179,58 @@ public void WriteCilInstruction(IVisitorContext context, IlContext il, OpCode op public void WriteCilBranch(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null) { - var mappedOpCodeName = MapSystemReflectionOpCodeNameToSystemReflectionMetadata(branchOpCode); - context.Generate($"{il.VariableName}.Branch(ILOpCode.{mappedOpCodeName}, {targetLabel});"); + context.Generate(EmitCilBranchInstruction(context, il, branchOpCode, targetLabel, comment)); context.WriteNewLine(); } + public string EmitCilBranchInstruction(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null) + { + var mappedOpCodeName = MapSystemReflectionOpCodeNameToSystemReflectionMetadata(branchOpCode); + return $"{il.VariableName}.Branch(ILOpCode.{mappedOpCodeName}, {targetLabel});"; + } + public void DefineLabel(IVisitorContext context, IlContext il, string labelVariable) { context.Generate($"var {labelVariable} = {il.VariableName}.DefineLabel();"); context.WriteNewLine(); } + public string EmitDefineLabel(IVisitorContext context, IlContext il, string labelVariable) => $"var {labelVariable} = {il.VariableName}.DefineLabel();"; + public void MarkLabel(IVisitorContext context, IlContext il, string labelVariable) { context.Generate($"{il.VariableName}.MarkLabel({labelVariable});"); context.WriteNewLine(); } + + public string EmitMarkLabel(IVisitorContext context, IlContext il, string labelVariable) => $"{il.VariableName}.MarkLabel({labelVariable});"; public void AddMethodSemantics(IVisitorContext context, string targetVariable, string methodVariable, MethodKind methodKind) { // In SRM, properties/event methods are handled in IApiDriverDefinitionsFactory.Property(). } + public void WriteExceptionHandlers(IVisitorContext context, IlContext ilVar, IEnumerable exceptionHandlerTable) + { + foreach (var entry in exceptionHandlerTable) + { + if (entry.Kind == ExceptionHandlerKind.Catch) + { + context.Generate($"{ilVar.VariableName}.ControlFlowBuilder.AddCatchRegion({entry.TryStart}, {entry.TryEnd}, {entry.HandlerStart}, {entry.HandlerEnd}, {entry.CatchType});"); + context.WriteNewLine(); + } + else if (entry.Kind == ExceptionHandlerKind.Finally) + { + context.Generate($"{ilVar.VariableName}.ControlFlowBuilder.AddFinallyRegion({entry.TryStart}, {entry.TryEnd}, {entry.HandlerStart}, {entry.HandlerEnd});"); + context.WriteNewLine(); + } + else + { + throw new NotSupportedException($"Unhandled exception handler kind: {entry.Kind}"); + } + } + } + /// /// Maps Ldc_Ix => Ldc_ix, Ldc_Rx, Conv_Ix => Ldc_rx, etc. /// diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataMemberResolver.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataMemberResolver.cs index 2108565a..cfbe2e94 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataMemberResolver.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataMemberResolver.cs @@ -12,87 +12,116 @@ public class SystemReflectionMetadataMemberResolver(SystemReflectionMetadataCont { public string ResolveMethod(IMethodSymbol method) { + var methodRefVar = context.Naming.SyntheticVariable($"{method.ToValidVariableName()}", ElementKind.MemberReference); var toBeFound = method.AsRawMethodDefinitionVariable(VariableMemberKind.MethodReference); var found = context.DefinitionVariables.GetMethodVariable(toBeFound); if (found.IsValid) - return found.VariableName; - - var containingTypeRefVar= context.TypeResolver.ResolveAny(method.ContainingType, ResolveTargetKind.TypeReference); - var methodSignatureBlobVar = context.Naming.SyntheticVariable($"{method.ToValidVariableName()}BlobBuilder", ElementKind.MemberReference); - var methodRefVar = context.Naming.SyntheticVariable($"{method.ToValidVariableName()}", ElementKind.MemberReference); - - var methodSignatureVar = context.Naming.SyntheticVariable($"{method.Name}Signature", ElementKind.MemberReference); - context.DefinitionVariables.RegisterMethod(method.AsMethodDefinitionVariable(VariableMemberKind.MethodSignature, methodSignatureVar)); + { + methodRefVar = found.VariableName; + } + else + { + var methodSignatureVariableToFind = method.AsMethodDefinitionVariable(VariableMemberKind.MethodSignature); - var isInstanceMethod = !method.IsStatic && method.MethodKind != MethodKind.LocalFunction; // local functions are always declared as static (we don't support capturing variables) - context.Generate($$""" - var {{methodSignatureBlobVar}} = new BlobBuilder(); + var methodSignatureVarName = context.Naming.SyntheticVariable($"{method.Name}Signature", ElementKind.MemberReference); + var methodSignatureVar = FindOrRegisterVariable(context, method, methodSignatureVariableToFind, methodSignatureVarName, null!, static (ctx, method, methodSignatureVarName, _) => + { + var isInstanceMethod = !method.IsStatic && method.MethodKind != MethodKind.LocalFunction; // local functions are always declared as static (we don't support capturing variables) + var methodSignatureBlobVar = ctx.Naming.SyntheticVariable($"{method.ToValidVariableName()}BlobBuilder", ElementKind.MemberReference); + + ctx.Generate($$""" + var {{methodSignatureBlobVar}} = new BlobBuilder(); + + new BlobEncoder({{methodSignatureBlobVar}}). + MethodSignature(isInstanceMethod: {{isInstanceMethod.ToKeyword()}}, genericParameterCount: {{method.TypeArguments.Length}}). + Parameters({{method.Parameters.Length}}, + returnType => returnType.{{ctx.TypedTypeResolver.Resolve(method.OriginalDefinition.ReturnType, method.ToTypeResolutionContext())}}, + parameters => + { + {{ + string.Join('\n', + method.OriginalDefinition.Parameters.Select(p => $""" + parameters.AddParameter().{ctx.TypedTypeResolver.Resolve(p.Type, new TypeResolutionContext(ResolveTargetKind.Parameter, TypeResolutionOptionsFor(p)))}; + """))}} + }); + + var {{methodSignatureVarName}} = metadata.GetOrAddBlob({{methodSignatureBlobVar}}); + """); + ctx.WriteNewLine(); + }); + + var containingTypeRefVar = context.TypeResolver.Resolve(method.ContainingType, ResolveTargetKind.TypeReference); + context.Generate($$""" + var {{methodRefVar}} = metadata.AddMemberReference( + {{containingTypeRefVar}}, + metadata.GetOrAddString("{{method.MappedName()}}"), + {{methodSignatureVar.VariableName}}); + """); + + context.WriteNewLine(); + context.DefinitionVariables.RegisterMethod(toBeFound.WithVariableName(methodRefVar)); + } - new BlobEncoder({{methodSignatureBlobVar}}). - MethodSignature(isInstanceMethod: {{ isInstanceMethod.ToKeyword() }}). - Parameters({{method.Parameters.Length}}, - returnType => returnType.{{context.TypedTypeResolver.ResolveAny(method.ReturnType, method.ToTypeResolutionContext())}}, - parameters => + if (method.IsGenericMethod && (!method.IsDefinition || method.IsDefinedInCurrentAssembly(context))) + { + var tbf = method.AsRawMethodDefinitionVariable(VariableMemberKind.MethodInstantiation); + var instantiationVar = FindOrRegisterVariable(context, method, tbf, context.Naming.GenericInstance(method), methodRefVar, static (ctx, method, methodSpecificationVar, openMethodVar) => + { + ctx.Generate($$""" + MethodSpecificationHandle {{methodSpecificationVar}}; { + var tempMethodSignature = new BlobEncoder(new BlobBuilder()).MethodSpecificationSignature({{method.TypeArguments.Length}}); {{ - string.Join('\n', - method.OriginalDefinition.Parameters.Select(p => $""" - parameters - .AddParameter() - .{context.TypedTypeResolver.ResolveAny(p.Type, new TypeResolutionContext(ResolveTargetKind.Parameter, p.Type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None))}; - """))}} - }); - - var {{methodSignatureVar}} = metadata.GetOrAddBlob({{methodSignatureBlobVar}}); - var {{methodRefVar}} = metadata.AddMemberReference( - {{containingTypeRefVar}}, - metadata.GetOrAddString("{{method.MappedName()}}"), - {{methodSignatureVar}}); - """); - - context.WriteNewLine(); - - context.DefinitionVariables.RegisterMethod(toBeFound.WithVariableName(methodRefVar)); + string.Join('\n', method.TypeArguments.Select(typeArgument => $"tempMethodSignature.AddArgument().{ctx.TypeResolver.Resolve(typeArgument, ResolveTargetKind.GenericTypeArgument.ToTypeResolutionContext())}; // {typeArgument.Name}")) + }} + {{methodSpecificationVar}} = metadata.AddMethodSpecification({{openMethodVar}}, metadata.GetOrAddBlob(tempMethodSignature.Builder)); + } + """); + ctx.WriteNewLine(); + }); + + methodRefVar = instantiationVar.VariableName; + } return methodRefVar; } - public string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodNameForVariableRegistration, ResolvedType returnType, IReadOnlyList parameters, int typeParameterCountCount, MemberOptions options) + public string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodName, ResolvedType returnType, IReadOnlyList parameters, IReadOnlyList typeParameters, MemberOptions options) { var methodReferenceToFind = new MethodDefinitionVariable( VariableMemberKind.MethodReference, declaringTypeName, - methodNameForVariableRegistration, + methodName, parameters.Select(p => p.ElementType.Expression).ToArray(), - typeParameterCountCount); - - var found = context.DefinitionVariables.GetMethodVariable(methodReferenceToFind); + typeParameters.ToArray()); + + var found = LookupRegisteredMethod(declaringTypeName, methodName, parameters, typeParameters); if (found.IsValid) return found.VariableName; - - var methodSignatureBlobVar = context.Naming.SyntheticVariable($"{methodNameForVariableRegistration}_blobBuilder", ElementKind.MemberReference); - var methodSignatureVar = context.Naming.SyntheticVariable($"{methodNameForVariableRegistration}_Signature", ElementKind.MemberReference); - var methodRefVar = context.Naming.SyntheticVariable($"{methodNameForVariableRegistration}", ElementKind.MemberReference); - + + var safeMethodName = methodName.ToValidIdentifier(); + var methodSignatureBlobVar = context.Naming.SyntheticVariable($"{safeMethodName}_blobBuilder", ElementKind.MemberReference); + var methodSignatureVar = context.Naming.SyntheticVariable($"{safeMethodName}_Signature", ElementKind.MemberReference); + var methodRefVar = context.Naming.SyntheticVariable($"{safeMethodName}", ElementKind.MemberReference); + context.DefinitionVariables.RegisterMethod(new MethodDefinitionVariable( VariableMemberKind.MethodSignature, declaringTypeName, - methodNameForVariableRegistration, + methodName, parameters.Select(p => p.ElementType.Expression).ToArray(), - typeParameterCountCount, + typeParameters.ToArray(), methodSignatureVar)); - var requiredModifierOrEmpty = string.Empty; if ((options & MemberOptions.InitOnly) != 0) { var modifierVariable = context.TypedTypeResolver.Resolve(context.RoslynTypeSystem.ForType(typeof(IsExternalInit).FullName), ResolveTargetKind.TypeReference); requiredModifierOrEmpty = $"returnTypeEncoder.CustomModifiers().AddModifier({modifierVariable}, isOptional: false);"; } - + context.Generate($$""" var {{methodSignatureBlobVar}} = new BlobBuilder(); new BlobEncoder({{methodSignatureBlobVar}}). - MethodSignature(isInstanceMethod: {{(options != MemberOptions.Static).ToKeyword()}}). + MethodSignature(isInstanceMethod: {{(options != MemberOptions.Static).ToKeyword()}}, genericParameterCount: {{typeParameters.Count}}). Parameters({{parameters.Count}}, returnTypeEncoder => { @@ -102,17 +131,17 @@ public string ResolveMethod(string declaringTypeName, string declaringTypeVariab parameters => { {{ - string.Join('\n', parameters.Select(p => $"parameters.AddParameter().{p.ElementType};")) + string.Join('\n', parameters.Select(p => $"parameters.AddParameter().{ p.ElementTypeResolver?.Invoke(context, p) ?? p.ElementType.Expression };")) }} }); var {{methodSignatureVar}} = metadata.GetOrAddBlob({{methodSignatureBlobVar}}); var {{methodRefVar}} = metadata.AddMemberReference( {{declaringTypeVariable}}, - metadata.GetOrAddString("{{methodNameForVariableRegistration}}"), + metadata.GetOrAddString("{{methodName}}"), {{methodSignatureVar}}); """); - + context.WriteNewLine(); context.DefinitionVariables.RegisterMethod(methodReferenceToFind.WithVariableName(methodRefVar)); @@ -124,28 +153,28 @@ public string ResolveDefaultConstructor(ITypeSymbol baseType, string _/*derivedT var parameterlessCtor = baseType.GetMembers(".ctor").OfType().Single(m => m.Parameters.Length == 0); return ResolveMethod(parameterlessCtor); } - + public string ResolveField(IFieldSymbol field) { var found = context.DefinitionVariables.GetVariable(field.Name, VariableMemberKind.Field, field.ContainingType.ToDisplayString()); if (found.IsValid) return found.VariableName; - var resolvedDeclaringType = context.TypeResolver.ResolveAny(field.ContainingType, new TypeResolutionContext(ResolveTargetKind.TypeReference, TypeResolutionOptions.None)); + var resolvedDeclaringType = context.TypeResolver.Resolve(field.ContainingType, new TypeResolutionContext(ResolveTargetKind.TypeReference, TypeResolutionOptions.None)); var fieldSignatureVarName = context.Naming.SyntheticVariable($"{field.ToValidVariableName()}_Signature", ElementKind.MemberReference); var fieldRefVarName = context.Naming.SyntheticVariable(field.Name, ElementKind.Field); var typeResolver = (SystemReflectionMetadataTypeResolver) context.TypeResolver; context.Generate($""" BlobBuilder {fieldSignatureVarName} = new(); - new BlobEncoder({fieldSignatureVarName}).Field().{typeResolver.ResolveAny(field.Type, new TypeResolutionContext(ResolveTargetKind.Field, field.RefKind != RefKind.None ? TypeResolutionOptions.IsByRef : TypeResolutionOptions.None))}; + new BlobEncoder({fieldSignatureVarName}).Field().{typeResolver.Resolve(field.Type, new TypeResolutionContext(ResolveTargetKind.Field, field.RefKind != RefKind.None ? TypeResolutionOptions.IsByRef : TypeResolutionOptions.None))}; var {fieldRefVarName} = metadata.AddMemberReference({resolvedDeclaringType}, metadata.GetOrAddString("{field.Name}"), metadata.GetOrAddBlob({fieldSignatureVarName})); """); - + context.WriteNewLine(); - + context.DefinitionVariables.RegisterNonMethod(field.ContainingType.ToDisplayString(), field.Name, VariableMemberKind.Field, fieldRefVarName); - + return fieldRefVarName; } @@ -153,4 +182,65 @@ public string ResolveEventField(IEventSymbol aEvent) { throw new NotImplementedException(); } + + public string ImportReference(string expression) => expression; // In SRM this is a noop + + public string MakeGeneticInstanceMethod(string methodReferenceVariable, string methodName, IReadOnlyList resolvedTypeArguments) + { + var tbf = new MethodDefinitionVariable(VariableMemberKind.MethodInstantiation, "parent?", methodName, [], resolvedTypeArguments.Select(rt => rt.Expression).ToArray()); + var methodVariableName = context.Naming.SyntheticVariable(methodName, ElementKind.GenericInstance); + var instantiationVar = FindOrRegisterVariable(context, resolvedTypeArguments, tbf, methodVariableName, methodReferenceVariable, static (ctx, typeArguments, methodSpecificationVar, openMethodVar) => + { + ctx.Generate($$""" + MethodSpecificationHandle {{methodSpecificationVar}}; + { + var tempMethodSignature = new BlobEncoder(new BlobBuilder()).MethodSpecificationSignature({{typeArguments.Count}}); + {{ + string.Join('\n', typeArguments.Select(typeArgument => $"tempMethodSignature.AddArgument().{typeArgument};")) + }} + {{methodSpecificationVar}} = metadata.AddMethodSpecification({{openMethodVar}}, metadata.GetOrAddBlob(tempMethodSignature.Builder)); + } + """); + ctx.WriteNewLine(); + }); + + return instantiationVar.VariableName; + } + + public string MapSpecificAttributes(IMethodSymbol method) => method.IsExtern ? "MethodAttributes.PinvokeImpl" : string.Empty; + + internal MethodDefinitionVariable LookupRegisteredMethod(string declaringTypeName, string methodName, IReadOnlyList parameters, IReadOnlyList typeParameters) + { + var methodReferenceToFind = new MethodDefinitionVariable( + VariableMemberKind.MethodReference, + declaringTypeName, + methodName, + parameters.Select(p => p.ElementType.Expression).ToArray(), + typeParameters.ToArray()); + + var found = context.DefinitionVariables.GetMethodVariable(methodReferenceToFind); + return found.IsValid ? (MethodDefinitionVariable) found : MethodDefinitionVariable.MethodNotFound; + } + + #region Non public members + private static DefinitionVariable FindOrRegisterVariable(SystemReflectionMetadataContext context, TTypeArgumentsState typeArgumentsState, MethodDefinitionVariable tbf, string variableNameToRegister, string openMethodVar, Action action) + { + var instantiationVar = context.DefinitionVariables.GetMethodVariable(tbf); + if (!instantiationVar.IsValid) + { + action(context, typeArgumentsState, variableNameToRegister, openMethodVar); + instantiationVar = context.DefinitionVariables.RegisterMethod(tbf.WithVariableName(variableNameToRegister)); + } + + return instantiationVar; + } + private static TypeResolutionOptions TypeResolutionOptionsFor(IParameterSymbol parameter) + { + var byRefState= parameter.RefKind != RefKind.None + ? TypeResolutionOptions.IsByRef + : TypeResolutionOptions.None; + + return (parameter.Type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None) | byRefState; + } + #endregion } diff --git a/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataTypeResolver.cs b/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataTypeResolver.cs index bdffb4ca..bf59c311 100644 --- a/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataTypeResolver.cs +++ b/Cecilifier.ApiDriver.SystemReflectionMetadata/TypeSystem/SystemReflectionMetadataTypeResolver.cs @@ -10,17 +10,40 @@ namespace Cecilifier.ApiDriver.SystemReflectionMetadata.TypeSystem; public class SystemReflectionMetadataTypeResolver(SystemReflectionMetadataContext context) : TypeResolverBase(context) { - public override ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext) + protected override ResolvedType ResolveNestedTypeCore(INamedTypeSymbol type, in TypeResolutionContext resolutionContext) { - var memberRefVar = _context.DefinitionVariables.GetVariable(type.ToDisplayString(), VariableMemberKind.Type, type.ContainingSymbol.ToDisplayString()); - var memberRefVarName = memberRefVar.IsValid - ? memberRefVar.VariableName - : _context.Naming.SyntheticVariable(type.ToValidVariableName(), ElementKind.MemberReference); - if (!memberRefVar.IsValid) - { - _context.DefinitionVariables.RegisterNonMethod(type.ContainingSymbol.ToDisplayString(), type.ToDisplayString(), VariableMemberKind.Type, memberRefVarName); - } - + var enclosingType = Resolve(type.ContainingType, new TypeResolutionContext(ResolveTargetKind.TypeReference, TypeResolutionOptions.OpenGenericType)); + + var variableName = _context.Naming.SyntheticVariable(type.ToValidVariableName(), ElementKind.MemberReference); + _context.Generate($""" + var {variableName} = metadata.AddTypeReference({enclosingType.Expression}, default(StringHandle), metadata.GetOrAddString("{type.Name}")); + """); + _context.WriteNewLine(); + return variableName; + } + + protected override ResolvedType ResolveTypeParameter(ITypeSymbol type, in TypeResolutionContext resolutionContext) + { + if (type is not ITypeParameterSymbol typeParameterSymbol) + return null; + + return ResolvedType.FromDetails( + new ResolvedTypeDetails() + .WithTypeEncoder(TypeEncoderFor(in resolutionContext)) + .WithMethodBuilder(GenericParameterExpressionFor(typeParameterSymbol))); + } + + public override ResolvedType ResolveTypeParameter(ResolvedType genericTypeParameter, TypeParameterKind typeParameterKind, in TypeResolutionContext resolutionContext) + { + return ResolvedType.FromDetails( + new ResolvedTypeDetails() + .WithTypeEncoder(TypeEncoderFor(in resolutionContext)) + .WithMethodBuilder(GenericParameterExpressionFor(genericTypeParameter, typeParameterKind))); + } + + protected override ResolvedType ResolveFromAssembly(ITypeSymbol type, in TypeResolutionContext resolutionContext) + { + var memberRefVarName = _context.Naming.SyntheticVariable(type.ToValidVariableName(), ElementKind.MemberReference); var assemblyReferenceName = _context.AssemblyResolver.Resolve(_context, type.ContainingAssembly); _context.Generate($""" @@ -31,7 +54,9 @@ public override ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext """); _context.WriteNewLine(); - if (resolutionContext.TargetKind is ResolveTargetKind.TypeReference || type is INamedTypeSymbol { IsGenericType: true }) + RegisterVariableIfNeeded(type, memberRefVarName, in resolutionContext); + + if (resolutionContext.TargetKind is ResolveTargetKind.TypeReference or ResolveTargetKind.GenericTypeParameterConstraint || type is INamedTypeSymbol { IsGenericType: true }) return memberRefVarName; return ApplySpecificSyntax(memberRefVarName, in resolutionContext); @@ -66,7 +91,7 @@ private ResolvedType ResolveForTargetKind(ITypeSymbol type, in TypeResolutionCon return ResolvedType.FromDetails( resolvedTypeDetails .WithTypeEncoder(TypeEncoderFor(in resolutionContext)) - .WithMethodBuilder($"Type({ResolveAny(type, TypeResolution.DefaultContext)}, isValueType: {type.IsValueType.ToKeyword()})")); + .WithMethodBuilder($"Type({Resolve(type, TypeResolution.DefaultContext)}, isValueType: {type.IsValueType.ToKeyword()})")); } public override ResolvedType ApplySpecificSyntax(string variableName, in TypeResolutionContext resolutionContext) @@ -80,94 +105,171 @@ public override ResolvedType ApplySpecificSyntax(string variableName, in TypeRes public override ResolvedType ResolvePredefinedType(ITypeSymbol type, in TypeResolutionContext resolutionContext) { - if (resolutionContext.TargetKind == ResolveTargetKind.TypeReference) - { - var mangledTypeVariableName = $"resolved-type$=>{type.ToDisplayString()}"; - var found = _context.DefinitionVariables.GetVariable(mangledTypeVariableName, VariableMemberKind.Type, type.ContainingAssembly.ToDisplayString()); - if (found.IsValid) - return found.VariableName; - - var resolvedTypeVariable = _context.Naming.SyntheticVariable(type.Name, ElementKind.MemberReference); - _context.DefinitionVariables.RegisterNonMethod(type.ContainingAssembly.ToDisplayString(), mangledTypeVariableName, VariableMemberKind.Type, resolvedTypeVariable); - - _context.Generate($""" - var {resolvedTypeVariable} = metadata.AddTypeReference({_context.AssemblyResolver.Resolve(_context, _context.RoslynTypeSystem.SystemObject.ContainingAssembly)}, metadata.GetOrAddString("{type.ContainingNamespace.Name}"), metadata.GetOrAddString("{type.Name}")); - """); - - _context.WriteNewLine(); - return resolvedTypeVariable; - } - return ResolveForTargetKind(type, resolutionContext); + if (resolutionContext.TargetKind != ResolveTargetKind.TypeReference) + return ResolveForTargetKind(type, resolutionContext); + + var mangledTypeVariableName = $"resolved-type$=>{type.ToDisplayString()}"; + var found = _context.DefinitionVariables.GetVariable(mangledTypeVariableName, VariableMemberKind.Type, type.ContainingAssembly.ToDisplayString()); + if (found.IsValid) + return found.VariableName; + + var resolvedTypeVariable = _context.Naming.SyntheticVariable(type.Name, ElementKind.MemberReference); + _context.DefinitionVariables.RegisterNonMethod(type.ContainingAssembly.ToDisplayString(), mangledTypeVariableName, VariableMemberKind.Type, resolvedTypeVariable); + + _context.Generate($""" + var {resolvedTypeVariable} = metadata.AddTypeReference({_context.AssemblyResolver.Resolve(_context, _context.RoslynTypeSystem.SystemObject.ContainingAssembly)}, metadata.GetOrAddString("{type.ContainingNamespace.Name}"), metadata.GetOrAddString("{type.MetadataName}")); + """); + + _context.WriteNewLine(); + return resolvedTypeVariable; } public override ResolvedType ResolveLocalVariableType(ITypeSymbol type, in TypeResolutionContext context) { var resolved = base.ResolveLocalVariableType(type, in context); + if (!resolved) + return resolved; - if (resolved && context.TargetKind == ResolveTargetKind.Instruction) + if (context.TargetKind == ResolveTargetKind.Instruction) { return new ResolvedType($"MetadataTokens.GetToken({resolved})"); } - - if (resolved && context.TargetKind != ResolveTargetKind.TypeReference && ((context.TargetKind != ResolveTargetKind.Field && context.TargetKind != ResolveTargetKind.ReturnType && context.TargetKind != ResolveTargetKind.LocalVariable) || type is not INamedTypeSymbol { IsGenericType: true })) - { - var methodBuilder = context.TargetKind == ResolveTargetKind.GenericTypeArgument || type.TypeKind == TypeKind.TypeParameter - ? $"GenericTypeParameter({resolved.Expression})" - : $"Type({resolved.Expression}, isValueType: {context.Options.HasFlag(TypeResolutionOptions.IsValueType).ToKeyword()})"; - if ((context.TargetKind is ResolveTargetKind.Field or ResolveTargetKind.Parameter) && type is INamedTypeSymbol { IsGenericType: true }) - { - methodBuilder = resolved.Expression; - } + if (type.TypeKind == TypeKind.TypeParameter && context.TargetKind is ResolveTargetKind.GenericTypeParameterConstraint or ResolveTargetKind.TypeReference) + { + //TODO: Try to register/lookup a variable for the type parameter type specification to avoid duplication. + var typeParameterBlobEncoderVar = _context.Naming.SyntheticVariable(type.Name, ElementKind.GenericParameter); + var typeParameterTypeSpecificationVar = _context.Naming.SyntheticVariable(type.Name, ElementKind.GenericParameter); - return ResolvedType.FromDetails( - new ResolvedTypeDetails() - .WithTypeEncoder(TypeEncoderFor(in context)) - .WithMethodBuilder(methodBuilder)); + _context.Generate(StringExtensions.Indented($""" + var {typeParameterBlobEncoderVar} = new BlobEncoder(new BlobBuilder()); + {typeParameterBlobEncoderVar}.TypeSpecificationSignature().{GenericParameterExpressionFor((ITypeParameterSymbol) type)}; + TypeSpecificationHandle {typeParameterTypeSpecificationVar} = metadata.AddTypeSpecification(metadata.GetOrAddBlob({typeParameterBlobEncoderVar}.Builder)); + """)); + _context.WriteNewLine(); + return new ResolvedType(typeParameterTypeSpecificationVar); } + + if (context.TargetKind != ResolveTargetKind.GenericTypeArgument || type is not INamedTypeSymbol { IsGenericType: true }) + if (context.TargetKind != ResolveTargetKind.TypeReference && ((context.TargetKind != ResolveTargetKind.Field && context.TargetKind != ResolveTargetKind.ReturnType && context.TargetKind != ResolveTargetKind.LocalVariable) || type is not INamedTypeSymbol { IsGenericType: true })) + { + var methodBuilder = type.TypeKind == TypeKind.TypeParameter + ? GenericParameterExpressionFor((ITypeParameterSymbol) type) + : $"Type({resolved.Expression}, isValueType: {context.Options.HasFlag(TypeResolutionOptions.IsValueType).ToKeyword()})"; + + if (context.TargetKind is ResolveTargetKind.GenericTypeParameterConstraint || (context.TargetKind is ResolveTargetKind.Field or ResolveTargetKind.Parameter && type is INamedTypeSymbol { IsGenericType: true })) + { + return resolved.Expression; + } + + return ResolvedType.FromDetails( + new ResolvedTypeDetails() + .WithTypeEncoder(TypeEncoderFor(in context)) + .WithMethodBuilder(methodBuilder)); + } + return resolved; } - public override ResolvedType MakeGenericInstanceType(ResolvedType typeReference, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext) + public override ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext) { Buffer256 typeArgumentsBuffer = new(); ReadOnlySpan typeArguments = CollectTypeArguments(genericTypeSymbol, ref typeArgumentsBuffer); if (typeArguments.Length == 0) - return typeReference; + return openGenericType; - if (resolutionContext.TargetKind is ResolveTargetKind.Field or ResolveTargetKind.Parameter or ResolveTargetKind.ReturnType or ResolveTargetKind.LocalVariable) + if (resolutionContext.TargetKind is ResolveTargetKind.Field or ResolveTargetKind.Parameter or ResolveTargetKind.ReturnType or ResolveTargetKind.LocalVariable or ResolveTargetKind.GenericTypeArgument) { - var resolved = MakeGenericInstanceTypeForFieldDeclaration(typeReference, genericTypeSymbol, typeArguments); - if (resolved && resolutionContext.TargetKind is ResolveTargetKind.ReturnType or ResolveTargetKind.Field or ResolveTargetKind.LocalVariable) + var resolved = MakeGenericInstanceType(openGenericType, genericTypeSymbol, typeArguments); + if (resolved && resolutionContext.TargetKind is ResolveTargetKind.ReturnType or ResolveTargetKind.Field or ResolveTargetKind.LocalVariable or ResolveTargetKind.Parameter) { return ResolvedType.FromDetails( new ResolvedTypeDetails() .WithTypeEncoder(TypeEncoderFor(in resolutionContext)) .WithMethodBuilder(resolved.Expression)); } + return resolved; } + + var definitionVariable = _context.DefinitionVariables.GetOrRegisterNonMethodVariable(typeName, string.Empty, VariableMemberKind.None, + new TypeResolutionContext(ResolveTargetKind.None, TypeResolutionOptions.RegisterVariables), + typeArguments, + state => + { + var genericInstanceTypeVar = _context.Naming.SyntheticVariable($"{genericTypeSymbol.ToValidVariableName()}Instantiation", ElementKind.GenericInstance); + _context.Generate($$""" + TypeSpecificationHandle {{genericInstanceTypeVar}} = default; + { + var typeSpecificationSig = new BlobEncoder(new BlobBuilder()).TypeSpecificationSignature(); + var gti = typeSpecificationSig.GenericInstantiation({{openGenericType.Expression}}, {{state.Length}}, isValueType: {{genericTypeSymbol.IsValueType.ToKeyword()}}); + {{ + state.ToImmutableArray().Select( + targ => $"gti.AddArgument().{_context.TypedTypeResolver.Resolve(targ, ResolveTargetKind.GenericTypeArgument)};\n") + .Aggregate("", (acc, s) => acc + s) + }} + {{genericInstanceTypeVar}} = metadata.AddTypeSpecification(metadata.GetOrAddBlob(typeSpecificationSig.Builder)); + } + """); + _context.WriteNewLine(); + + return genericInstanceTypeVar; + }); - var genericInstanceTypeVar = context.Naming.SyntheticVariable($"{genericTypeSymbol.ToValidVariableName()}Instantiation", ElementKind.GenericInstance); - context.Generate($$""" - TypeSpecificationHandle {{genericInstanceTypeVar}} = default; - { - var be = new BlobEncoder(new BlobBuilder()); - var typeSpecificationSig = be.TypeSpecificationSignature(); - var gti = typeSpecificationSig.GenericInstantiation({{typeReference.Expression}}, {{typeArguments.Length}}, isValueType: {{genericTypeSymbol.IsValueType.ToKeyword()}}); - {{ - typeArguments.ToImmutableArray().Select( - targ => $"gti.AddArgument().{context.TypedTypeResolver.ResolveAny(targ, ResolveTargetKind.GenericTypeArgument)};\n") - .Aggregate("", (acc, s) => acc + s) - }} - {{genericInstanceTypeVar}} = metadata.AddTypeSpecification(metadata.GetOrAddBlob(be.Builder)); - } - """); - context.WriteNewLine(); - - return genericInstanceTypeVar; + definitionVariable.ThrowIfVariableIsNotValid(); + return definitionVariable.VariableName; + } + + public override ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, Span typeArguments, in TypeResolutionContext resolutionContext) + { + if (typeArguments.Length == 0) + return openGenericType; + + var isValueType = resolutionContext.Options.HasFlag(TypeResolutionOptions.IsValueType); + if (resolutionContext.TargetKind is ResolveTargetKind.Field or ResolveTargetKind.Parameter or ResolveTargetKind.ReturnType or ResolveTargetKind.LocalVariable or ResolveTargetKind.GenericTypeArgument) + { + var resolved = MakeGenericInstanceType(openGenericType, isValueType: isValueType, typeArguments); + if (resolved && resolutionContext.TargetKind is ResolveTargetKind.ReturnType or ResolveTargetKind.Field or ResolveTargetKind.LocalVariable or ResolveTargetKind.Parameter) + { + return ResolvedType.FromDetails( + new ResolvedTypeDetails() + .WithTypeEncoder(TypeEncoderFor(in resolutionContext)) + .WithMethodBuilder(resolved.Expression)); + } + + return resolved; + } + + var immutableArray = typeArguments.ToImmutableArray(); + var definitionVariable = _context.DefinitionVariables.GetOrRegisterNonMethodVariable( + typeName, + string.Empty, + VariableMemberKind.None, + new TypeResolutionContext(ResolveTargetKind.None, TypeResolutionOptions.RegisterVariables), + string.Empty, + _ => + { + var genericInstanceTypeVar = _context.Naming.SyntheticVariable("Instantiation", ElementKind.GenericInstance); + _context.Generate($$""" + TypeSpecificationHandle {{genericInstanceTypeVar}} = default; + { + var typeSpecificationSig = new BlobEncoder(new BlobBuilder()).TypeSpecificationSignature(); + var gti = typeSpecificationSig.GenericInstantiation({{openGenericType.Expression}}, {{immutableArray.Length}}, isValueType: {{isValueType.ToKeyword()}}); + {{ + immutableArray.Select(targ => $"gti.AddArgument().{targ.ToString()};\n").Aggregate("", (acc, s) => acc + s) + }} + {{genericInstanceTypeVar}} = metadata.AddTypeSpecification(metadata.GetOrAddBlob(typeSpecificationSig.Builder)); + } + """); + _context.WriteNewLine(); + + return genericInstanceTypeVar; + }); + + definitionVariable.ThrowIfVariableIsNotValid(); + return definitionVariable.VariableName; } public override ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResolutionContext resolutionContext) @@ -184,12 +286,15 @@ public override ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResol return ResolvedType.FromDetails( details.WithTypeEncoder(TypeEncoderForArrayElement(in resolutionContext)) - .WithMethodBuilder($"{methodBuilderByKind}.{ResolveAny(elementType, new TypeResolutionContext(ResolveTargetKind.ArrayElementType, resolutionContext.Options))}")); + .WithMethodBuilder($"{methodBuilderByKind}.{Resolve(elementType, new TypeResolutionContext(ResolveTargetKind.ComposedElementType, resolutionContext.Options))}")); } protected override ResolvedType MakePointerType(ITypeSymbol pointerType, in TypeResolutionContext resolutionContext) { - throw new NotImplementedException(); + var details = new ResolvedTypeDetails(); + return ResolvedType.FromDetails( + details.WithTypeEncoder(TypeEncoderFor(in resolutionContext)) + .WithMethodBuilder($"Pointer().{Resolve(pointerType, new TypeResolutionContext(ResolveTargetKind.ComposedElementType, resolutionContext.Options))}")); } protected override ResolvedType MakeFunctionPointerType(IFunctionPointerTypeSymbol functionPointer, in TypeResolutionContext resolutionContext) @@ -197,6 +302,8 @@ protected override ResolvedType MakeFunctionPointerType(IFunctionPointerTypeSymb throw new NotImplementedException(); } + public override ResolvedType MakeByRefType(in ResolvedType resolvedType) => resolvedType; // noop in SRM. ByRef types are handled during type resolution. + private static string TypeEncoderFor(in TypeResolutionContext resolutionContext) { if (resolutionContext.TargetKind == ResolveTargetKind.Instruction) @@ -207,7 +314,8 @@ private static string TypeEncoderFor(in TypeResolutionContext resolutionContext) { ResolveTargetKind.None => "", ResolveTargetKind.GenericTypeArgument => "", - ResolveTargetKind.ArrayElementType => "", + ResolveTargetKind.GenericTypeParameterConstraint => "", + ResolveTargetKind.ComposedElementType => "", ResolveTargetKind.AttributeNamedArgument or ResolveTargetKind.AttributeArgument => "ScalarType()%", _ => $"Type(isByRef: {isByRef})%", }; @@ -223,25 +331,54 @@ private static string TypeEncoderForArrayElement(in TypeResolutionContext resolu { ResolveTargetKind.AttributeNamedArgument => "", ResolveTargetKind.None => "", - ResolveTargetKind.ArrayElementType => "", + ResolveTargetKind.ComposedElementType => "", _ => $"Type(isByRef: {isByRef})%", }; } - //TODO: Method name is misleading. It is being also used for parameters/return types. What is the common thing about those - private ResolvedType MakeGenericInstanceTypeForFieldDeclaration(ResolvedType typeReference, INamedTypeSymbol genericTypeSymbol, ReadOnlySpan typeArguments) + private ResolvedType MakeGenericInstanceType(ResolvedType openGenericType, INamedTypeSymbol genericTypeSymbol, ReadOnlySpan typeArguments) { var ret = $$""" WithSignatureTypeEncoder(typeSignatureEncoder => { - var gi = typeSignatureEncoder.GenericInstantiation({{typeReference.Expression}}, {{typeArguments.Length}}, isValueType: {{genericTypeSymbol.IsValueType.ToKeyword()}}); + var gi = typeSignatureEncoder.GenericInstantiation({{openGenericType.Expression}}, {{typeArguments.Length}}, isValueType: {{genericTypeSymbol.IsValueType.ToKeyword()}}); {{ typeArguments.ToImmutableArray().Select( - targ => $"gi.AddArgument().{context.TypedTypeResolver.ResolveAny(targ, ResolveTargetKind.GenericTypeArgument)};\n ") + targ => $"gi.AddArgument().{_context.TypedTypeResolver.Resolve(targ, ResolveTargetKind.GenericTypeArgument)};\n ") .Aggregate("", (acc, s) => acc + s) }} }) """; return ret; } + + private ResolvedType MakeGenericInstanceType(ResolvedType openGenericType, bool isValueType, ReadOnlySpan typeArguments) + { + var ret = $$""" + WithSignatureTypeEncoder(typeSignatureEncoder => + { + var gi = typeSignatureEncoder.GenericInstantiation({{openGenericType.Expression}}, {{typeArguments.Length}}, isValueType: {{isValueType.ToKeyword()}}); + {{ + typeArguments.ToImmutableArray().Select(targ => $"gi.AddArgument().{targ};\n ").Aggregate("", (acc, s) => acc + s) + }} + }) + """; + return ret; + } + + private void RegisterVariableIfNeeded(ITypeSymbol type, string variableName, in TypeResolutionContext resolutionContext) + { + if (!resolutionContext.Options.HasFlag(TypeResolutionOptions.RegisterVariables)) + return; + + _context.DefinitionVariables.RegisterNonMethod(type.ContainingSymbol.OriginalDefinition.ToDisplayString(), type.OriginalDefinition.ToDisplayString(), VariableMemberKind.Type, variableName); + } + + private string GenericParameterExpressionFor(ITypeParameterSymbol typeParameter) => typeParameter.TypeParameterKind == TypeParameterKind.Type + ? $"GenericTypeParameter({typeParameter.Ordinal})" + : $"GenericMethodTypeParameter({typeParameter.Ordinal})"; + + private string GenericParameterExpressionFor(ResolvedType genericTypeParameter, TypeParameterKind typeParameterKind)=> typeParameterKind == TypeParameterKind.Type + ? $"GenericTypeParameter({genericTypeParameter})" + : $"GenericMethodTypeParameter({genericTypeParameter})"; } diff --git a/Cecilifier.Benchmarks/Cecilifier.Benchmarks.csproj b/Cecilifier.Benchmarks/Cecilifier.Benchmarks.csproj index 4bcc0404..5cff76cc 100644 --- a/Cecilifier.Benchmarks/Cecilifier.Benchmarks.csproj +++ b/Cecilifier.Benchmarks/Cecilifier.Benchmarks.csproj @@ -1,14 +1,15 @@ + + Exe - net9.0 enable enable - + diff --git a/Cecilifier.Benchmarks/Program.cs b/Cecilifier.Benchmarks/Program.cs index daf3448b..2b4556ce 100644 --- a/Cecilifier.Benchmarks/Program.cs +++ b/Cecilifier.Benchmarks/Program.cs @@ -13,7 +13,7 @@ public static void Main(string[] args) public class CecilifierExtensionsBenchmarks { [ParamsSource(nameof(PascalCaseValues))] - private string PascalCaseValue { get; set; } = string.Empty; + public string PascalCaseValue { get; set; } = string.Empty; [Benchmark(Baseline = true)] public string PascalCaseNaive() diff --git a/Cecilifier.Common.props b/Cecilifier.Common.props index 5173d869..e877ca5a 100644 --- a/Cecilifier.Common.props +++ b/Cecilifier.Common.props @@ -1,12 +1,12 @@ - net9.0 + net10.0 13 - 2.27.0 + 2.28.0 true true en - \ No newline at end of file + diff --git a/Cecilifier.CommonPackages.props b/Cecilifier.CommonPackages.props index a8b4dfc0..243df797 100644 --- a/Cecilifier.CommonPackages.props +++ b/Cecilifier.CommonPackages.props @@ -1,7 +1,7 @@ - - - 0.11.6 + + + diff --git a/Cecilifier.Core.Tests/Cecilifier.Core.Tests.csproj b/Cecilifier.Core.Tests/Cecilifier.Core.Tests.csproj index c682880d..0b4e866e 100644 --- a/Cecilifier.Core.Tests/Cecilifier.Core.Tests.csproj +++ b/Cecilifier.Core.Tests/Cecilifier.Core.Tests.csproj @@ -13,14 +13,14 @@ $(NoWarn);NU1603 - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/Cecilifier.Core.Tests/Framework/AssemblyDiff/StrictMethodDiffVisitor.cs b/Cecilifier.Core.Tests/Framework/AssemblyDiff/StrictMethodDiffVisitor.cs index c9cf32e6..d069af5a 100644 --- a/Cecilifier.Core.Tests/Framework/AssemblyDiff/StrictMethodDiffVisitor.cs +++ b/Cecilifier.Core.Tests/Framework/AssemblyDiff/StrictMethodDiffVisitor.cs @@ -76,12 +76,20 @@ public bool VisitGenerics(MethodDefinition source, MethodDefinition target) public bool VisitMemberProperties(MethodDefinition sourceMethod, MethodDefinition targetMethod) { - if (sourceMethod.IsStatic == targetMethod.IsStatic && sourceMethod.HasThis == targetMethod.HasThis) - return true; - - output.WriteLine($"Methods have mismatching staticness\n\tExpected: {sourceMethod.FullName} (IsStatic={sourceMethod.IsStatic}, HasThis={sourceMethod.HasThis})\n\tActual: {targetMethod.FullName} (IsStatic={targetMethod.IsStatic}, HasThis={targetMethod.HasThis})"); - return false; - + var ret = true; + if (sourceMethod.IsStatic != targetMethod.IsStatic || sourceMethod.HasThis != targetMethod.HasThis) + { + output.WriteLine($"Methods have mismatching staticness\n\tExpected: {sourceMethod.FullName} (IsStatic={sourceMethod.IsStatic}, HasThis={sourceMethod.HasThis})\n\tActual: {targetMethod.FullName} (IsStatic={targetMethod.IsStatic}, HasThis={targetMethod.HasThis})"); + ret = false; + } + + if (sourceMethod.ImplAttributes != targetMethod.ImplAttributes) + { + output.WriteLine($"Methods have mismatching ImplAttributes\n\tExpected: {sourceMethod.FullName} (ImplAttributes={sourceMethod.ImplAttributes})\n\tActual: {targetMethod.FullName} (ImplAttributes={targetMethod.ImplAttributes})"); + ret = false; + } + + return ret; } private string FormatLocalVariables(Collection variables) diff --git a/Cecilifier.Core.Tests/Framework/Attributes/DisableForContextAttribute.cs b/Cecilifier.Core.Tests/Framework/Attributes/DisableForContextAttribute.cs new file mode 100644 index 00000000..5948637d --- /dev/null +++ b/Cecilifier.Core.Tests/Framework/Attributes/DisableForContextAttribute.cs @@ -0,0 +1,20 @@ +using Cecilifier.Core.AST; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; + +namespace Cecilifier.Core.Tests.Framework.Attributes; + +internal class DisableForContextAttribute : FilterByContextBase, IApplyToTest where TContext : IVisitorContext +{ + public void ApplyToTest(Test test) + { + if (!HasMatchingContext(test)) + { + test.RunState = RunState.Runnable; + return; + } + + test.RunState = RunState.Ignored; + test.Properties[PropertyNames.SkipReason].Add(IgnoreReason); + } +} diff --git a/Cecilifier.Core.Tests/Framework/Attributes/FilterByContextBase.cs b/Cecilifier.Core.Tests/Framework/Attributes/FilterByContextBase.cs index 6e1d0698..071f7233 100644 --- a/Cecilifier.Core.Tests/Framework/Attributes/FilterByContextBase.cs +++ b/Cecilifier.Core.Tests/Framework/Attributes/FilterByContextBase.cs @@ -1,3 +1,4 @@ +//#define RUN_TESTS_ANYWAY #nullable enable using System; using System.Collections.Generic; @@ -26,6 +27,9 @@ protected bool HasMatchingContext(Test test) protected bool DisableTestIfNotApplicable(ITest test, string testName) { +#if RUN_TESTS_ANYWAY + return false; +#else if (_enabledTests.Contains(testName)) return false; @@ -33,5 +37,6 @@ protected bool DisableTestIfNotApplicable(ITest test, string testName) test.Properties[PropertyNames.SkipReason].Add(IgnoreReason); return true; +#endif } } diff --git a/Cecilifier.Core.Tests/Framework/CecilifierTestBase.cs b/Cecilifier.Core.Tests/Framework/CecilifierTestBase.cs index 1baf69dc..de47d118 100644 --- a/Cecilifier.Core.Tests/Framework/CecilifierTestBase.cs +++ b/Cecilifier.Core.Tests/Framework/CecilifierTestBase.cs @@ -21,7 +21,7 @@ public record struct CecilifyResult(string CecilifiedCode, string CecilifiedAsse public class CecilifierTestBase where TContext : IVisitorContext { - private protected string cecilifiedCode; + private string cecilifiedCode; [SetUp] public void Setup() diff --git a/Cecilifier.Core.Tests/Framework/StringExtensions.cs b/Cecilifier.Core.Tests/Framework/StringExtensions.cs index cd2a3a1c..b37d57dd 100644 --- a/Cecilifier.Core.Tests/Framework/StringExtensions.cs +++ b/Cecilifier.Core.Tests/Framework/StringExtensions.cs @@ -6,7 +6,7 @@ internal static class StringExtensions { public static string GetPathOfTextResource(this string resourceName, string type) { - return GetPathOfResource(resourceName, "." + type + ".txt"); + return GetPathOfResource(resourceName, $".{type}.txt"); } public static string GetPathOfBinaryResource(this string resourceName, string type) diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/DoubleLocalVariableInitializationComplex.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/DoubleLocalVariableInitializationComplex.cs.il.srm deleted file mode 100644 index 7315aeb8..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/DoubleLocalVariableInitializationComplex.cs.il.srm +++ /dev/null @@ -1,28 +0,0 @@ -.locals init (System.Double V_0, System.Double V_1) -IL_0000: ldc.i4 41 -IL_0005: conv.r8 -IL_0006: stloc V_0 -IL_000a: nop -IL_000b: nop -IL_000c: ldc.i4 2 -IL_0011: ldarg.0 -IL_0012: add -IL_0013: conv.r8 -IL_0014: stloc V_1 -IL_0018: nop -IL_0019: nop -IL_001a: ldloc V_0 -IL_001e: nop -IL_001f: nop -IL_0020: ldloc V_1 -IL_0024: nop -IL_0025: nop -IL_0026: add -IL_0027: ldarg.0 -IL_0028: ldarg.1 -IL_0029: ldarg.0 -IL_002a: conv.r8 -IL_002b: add -IL_002c: call System.Double DoubleLocalVariableInitializationComplex::Complex(System.Int32,System.Double) -IL_0031: add -IL_0032: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/IndexExpression.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/IndexExpression.cs.il.srm deleted file mode 100644 index 687a3445..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/IndexExpression.cs.il.srm +++ /dev/null @@ -1,16 +0,0 @@ -.locals init (System.Index V_0) -IL_0000: ldc.i4 42 -IL_0005: call System.Index System.Index::op_Implicit(System.Int32) -IL_000a: stloc V_0 -IL_000e: nop -IL_000f: nop -IL_0010: ldarg.2 -IL_0011: ldloca V_0 -IL_0015: nop -IL_0016: nop -IL_0017: ldarg.2 -IL_0018: ldlen -IL_0019: conv.i4 -IL_001a: call System.Int32 System.Index::GetOffset(System.Int32) -IL_001f: ldelem.i4 -IL_0020: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/MultipleLocalVariableAssignment.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/MultipleLocalVariableAssignment.cs.il.srm deleted file mode 100644 index 0d555741..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/MultipleLocalVariableAssignment.cs.il.srm +++ /dev/null @@ -1,28 +0,0 @@ -.locals init (System.Int32 V_0, System.Int32 V_1, System.String V_2) -IL_0000: ldarg.0 -IL_0001: stloc V_0 -IL_0005: nop -IL_0006: nop -IL_0007: ldarg.0 -IL_0008: stloc V_1 -IL_000c: nop -IL_000d: nop -IL_000e: ldarg.1 -IL_000f: stloc V_2 -IL_0013: nop -IL_0014: nop -IL_0015: ldstr "Use local variables to avoid CS compiler optimization..." -IL_001a: call System.Void System.Console::WriteLine(System.String) -IL_001f: ldloc V_2 -IL_0023: nop -IL_0024: nop -IL_0025: call System.Void System.Console::WriteLine(System.String) -IL_002a: ldloc V_0 -IL_002e: nop -IL_002f: nop -IL_0030: call System.Void System.Console::WriteLine(System.Int32) -IL_0035: ldloc V_1 -IL_0039: nop -IL_003a: nop -IL_003b: call System.Void System.Console::WriteLine(System.Int32) -IL_0040: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Add2.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Add2.cs.il.srm deleted file mode 100644 index 3b06961a..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Add2.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.String V_0) -IL_0000: ldarg.0 -IL_0001: ldc.i4 10 -IL_0006: box System.Int32 -IL_000b: call System.String System.String::Concat(System.Object,System.Object) -IL_0010: stloc V_0 -IL_0014: nop -IL_0015: nop -IL_0016: ldstr "constant" -IL_001b: ldarg.1 -IL_001c: box System.Int32 -IL_0021: call System.String System.String::Concat(System.Object,System.Object) -IL_0026: stloc V_0 -IL_002a: nop -IL_002b: nop -IL_002c: ldloc V_0 -IL_0030: nop -IL_0031: nop -IL_0032: call System.Void System.Console::WriteLine(System.String) -IL_0037: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementField.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementField.cs.il.srm deleted file mode 100644 index 1badd4e5..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementField.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: stfld System.Int32 PostDecrementField::f -IL_0007: ldarg.0 -IL_0008: ldfld System.Int32 PostDecrementField::f -IL_000d: stloc V_0 -IL_0011: nop -IL_0012: nop -IL_0013: ldloc V_0 -IL_0017: nop -IL_0018: nop -IL_0019: ldarg.0 -IL_001a: ldloc V_0 -IL_001e: nop -IL_001f: nop -IL_0020: ldc.i4.1 -IL_0021: sub -IL_0022: stfld System.Int32 PostDecrementField::f -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementLocal.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementLocal.cs.il.srm deleted file mode 100644 index 4466b015..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementLocal.cs.il.srm +++ /dev/null @@ -1,15 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.1 -IL_0001: stloc V_0 -IL_0005: nop -IL_0006: nop -IL_0007: ldloc V_0 -IL_000b: nop -IL_000c: nop -IL_000d: dup -IL_000e: ldc.i4.1 -IL_000f: sub -IL_0010: stloc V_0 -IL_0014: nop -IL_0015: nop -IL_0016: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.srm deleted file mode 100644 index 2fa84bd2..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.srm +++ /dev/null @@ -1,10 +0,0 @@ -.locals () -IL_0000: ldarg.1 -IL_0001: dup -IL_0002: ldc.i4.1 -IL_0003: sub -IL_0004: starg.s 1 -IL_0006: nop -IL_0007: nop -IL_0008: nop -IL_0009: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.txt index f80189f2..be53a278 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.il.txt @@ -1,4 +1,4 @@ -.locals init () +.locals init (System.Int32 V_0) IL_0000: ldarg.1 IL_0001: dup IL_0002: ldc.i4.1 diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.txt index 3724754e..16e83ff2 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementParam.cs.txt @@ -2,6 +2,7 @@ { int M(int p) { + int dummy; // here only to ensure SRM will emit a `init` directive for the method. return p--; } } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementProp.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementProp.cs.il.srm deleted file mode 100644 index d4012808..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostDecrementProp.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: call System.Void PostDecrementProp::set_Prop(System.Int32) -IL_0007: ldarg.0 -IL_0008: call System.Int32 PostDecrementProp::get_Prop() -IL_000d: stloc V_0 -IL_0011: nop -IL_0012: nop -IL_0013: ldloc V_0 -IL_0017: nop -IL_0018: nop -IL_0019: ldarg.0 -IL_001a: ldloc V_0 -IL_001e: nop -IL_001f: nop -IL_0020: ldc.i4.1 -IL_0021: sub -IL_0022: call System.Void PostDecrementProp::set_Prop(System.Int32) -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementField.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementField.cs.il.srm deleted file mode 100644 index 7926c6f4..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementField.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: stfld System.Int32 PostIncrementField::f -IL_0007: ldarg.0 -IL_0008: ldfld System.Int32 PostIncrementField::f -IL_000d: stloc V_0 -IL_0011: nop -IL_0012: nop -IL_0013: ldloc V_0 -IL_0017: nop -IL_0018: nop -IL_0019: ldarg.0 -IL_001a: ldloc V_0 -IL_001e: nop -IL_001f: nop -IL_0020: ldc.i4.1 -IL_0021: add -IL_0022: stfld System.Int32 PostIncrementField::f -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementLocal.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementLocal.cs.il.srm deleted file mode 100644 index 491b35b4..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementLocal.cs.il.srm +++ /dev/null @@ -1,21 +0,0 @@ -.locals init (System.Int32 V_0, System.Int32 V_1) -IL_0000: ldarg.1 -IL_0001: stloc V_0 -IL_0005: nop -IL_0006: nop -IL_0007: ldloc V_0 -IL_000b: nop -IL_000c: nop -IL_000d: dup -IL_000e: ldc.i4.1 -IL_000f: add -IL_0010: stloc V_0 -IL_0014: nop -IL_0015: nop -IL_0016: stloc V_1 -IL_001a: nop -IL_001b: nop -IL_001c: ldloc V_1 -IL_0020: nop -IL_0021: nop -IL_0022: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementParam.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementParam.cs.il.srm deleted file mode 100644 index e9ef9e65..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementParam.cs.il.srm +++ /dev/null @@ -1,16 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.1 -IL_0001: dup -IL_0002: ldc.i4.1 -IL_0003: add -IL_0004: starg.s 1 -IL_0006: nop -IL_0007: nop -IL_0008: nop -IL_0009: stloc V_0 -IL_000d: nop -IL_000e: nop -IL_000f: ldloc V_0 -IL_0013: nop -IL_0014: nop -IL_0015: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementProp.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementProp.cs.il.srm deleted file mode 100644 index 38fda827..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PostIncrementProp.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: call System.Void PostIncrementProp::set_Prop(System.Int32) -IL_0007: ldarg.0 -IL_0008: call System.Int32 PostIncrementProp::get_Prop() -IL_000d: stloc V_0 -IL_0011: nop -IL_0012: nop -IL_0013: ldloc V_0 -IL_0017: nop -IL_0018: nop -IL_0019: ldarg.0 -IL_001a: ldloc V_0 -IL_001e: nop -IL_001f: nop -IL_0020: ldc.i4.1 -IL_0021: add -IL_0022: call System.Void PostIncrementProp::set_Prop(System.Int32) -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementField.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementField.cs.il.srm deleted file mode 100644 index e9ff6972..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementField.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: stfld System.Int32 PreDecrementField::f -IL_0007: ldarg.0 -IL_0008: ldfld System.Int32 PreDecrementField::f -IL_000d: ldc.i4.1 -IL_000e: sub -IL_000f: stloc V_0 -IL_0013: nop -IL_0014: nop -IL_0015: ldloc V_0 -IL_0019: nop -IL_001a: nop -IL_001b: ldarg.0 -IL_001c: ldloc V_0 -IL_0020: nop -IL_0021: nop -IL_0022: stfld System.Int32 PreDecrementField::f -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementLocal.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementLocal.cs.il.srm deleted file mode 100644 index 726c79d4..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementLocal.cs.il.srm +++ /dev/null @@ -1,15 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.1 -IL_0001: stloc V_0 -IL_0005: nop -IL_0006: nop -IL_0007: ldloc V_0 -IL_000b: nop -IL_000c: nop -IL_000d: ldc.i4.1 -IL_000e: sub -IL_000f: dup -IL_0010: stloc V_0 -IL_0014: nop -IL_0015: nop -IL_0016: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.srm deleted file mode 100644 index 266e8ff9..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.srm +++ /dev/null @@ -1,10 +0,0 @@ -.locals () -IL_0000: ldarg.1 -IL_0001: ldc.i4.1 -IL_0002: sub -IL_0003: dup -IL_0004: starg.s 1 -IL_0006: nop -IL_0007: nop -IL_0008: nop -IL_0009: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.txt index 7d605869..4b159d55 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.il.txt @@ -1,4 +1,4 @@ -.locals init () +.locals init (System.Int32 V_0) IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: sub diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.txt index 5f160433..e17e228a 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementParam.cs.txt @@ -2,6 +2,7 @@ { int M(int p) { + int dummy; // here only to ensure SRM will emit a `init` directive for the method. return --p; } } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementProp.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementProp.cs.il.srm deleted file mode 100644 index e50a4864..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreDecrementProp.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: call System.Void PreDecrementProp::set_Prop(System.Int32) -IL_0007: ldarg.0 -IL_0008: call System.Int32 PreDecrementProp::get_Prop() -IL_000d: ldc.i4.1 -IL_000e: sub -IL_000f: stloc V_0 -IL_0013: nop -IL_0014: nop -IL_0015: ldloc V_0 -IL_0019: nop -IL_001a: nop -IL_001b: ldarg.0 -IL_001c: ldloc V_0 -IL_0020: nop -IL_0021: nop -IL_0022: call System.Void PreDecrementProp::set_Prop(System.Int32) -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementField.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementField.cs.il.srm deleted file mode 100644 index 303740e2..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementField.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: stfld System.Int32 PreIncrementField::f -IL_0007: ldarg.0 -IL_0008: ldfld System.Int32 PreIncrementField::f -IL_000d: ldc.i4.1 -IL_000e: add -IL_000f: stloc V_0 -IL_0013: nop -IL_0014: nop -IL_0015: ldloc V_0 -IL_0019: nop -IL_001a: nop -IL_001b: ldarg.0 -IL_001c: ldloc V_0 -IL_0020: nop -IL_0021: nop -IL_0022: stfld System.Int32 PreIncrementField::f -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementLocal.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementLocal.cs.il.srm deleted file mode 100644 index 6e67b187..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementLocal.cs.il.srm +++ /dev/null @@ -1,15 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.1 -IL_0001: stloc V_0 -IL_0005: nop -IL_0006: nop -IL_0007: ldloc V_0 -IL_000b: nop -IL_000c: nop -IL_000d: ldc.i4.1 -IL_000e: add -IL_000f: dup -IL_0010: stloc V_0 -IL_0014: nop -IL_0015: nop -IL_0016: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.srm deleted file mode 100644 index bbe25361..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.srm +++ /dev/null @@ -1,10 +0,0 @@ -.locals () -IL_0000: ldarg.1 -IL_0001: ldc.i4.1 -IL_0002: add -IL_0003: dup -IL_0004: starg.s 1 -IL_0006: nop -IL_0007: nop -IL_0008: nop -IL_0009: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.txt index c5fc687b..b91cdb07 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.il.txt @@ -1,4 +1,4 @@ -.locals init () +.locals init (System.Int32 V_0) IL_0000: ldarg.1 IL_0001: ldc.i4.1 IL_0002: add diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.txt index 84463197..b188253a 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementParam.cs.txt @@ -2,6 +2,7 @@ { int M(int p) { + int dummy; // here only to ensure SRM will emit a `init` directive for the method. return ++p; } } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementProp.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementProp.cs.il.srm deleted file mode 100644 index 50fd65ce..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/Operators/Unary/PreIncrementProp.cs.il.srm +++ /dev/null @@ -1,20 +0,0 @@ -.locals init (System.Int32 V_0) -IL_0000: ldarg.0 -IL_0001: ldarg.1 -IL_0002: call System.Void PreIncrementProp::set_Prop(System.Int32) -IL_0007: ldarg.0 -IL_0008: call System.Int32 PreIncrementProp::get_Prop() -IL_000d: ldc.i4.1 -IL_000e: add -IL_000f: stloc V_0 -IL_0013: nop -IL_0014: nop -IL_0015: ldloc V_0 -IL_0019: nop -IL_001a: nop -IL_001b: ldarg.0 -IL_001c: ldloc V_0 -IL_0020: nop -IL_0021: nop -IL_0022: call System.Void PreIncrementProp::set_Prop(System.Int32) -IL_0027: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/TypeInferenceInDeclarations.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Expressions/TypeInferenceInDeclarations.cs.il.srm deleted file mode 100644 index a5d99e90..00000000 --- a/Cecilifier.Core.Tests/TestResources/Integration/Expressions/TypeInferenceInDeclarations.cs.il.srm +++ /dev/null @@ -1,26 +0,0 @@ -.locals init (System.Int32 V_0, System.Int32 V_1, System.String V_2) -IL_0000: ldc.i4 10 -IL_0005: stloc V_0 -IL_0009: nop -IL_000a: nop -IL_000b: ldloc V_0 -IL_000f: nop -IL_0010: nop -IL_0011: ldc.i4 10 -IL_0016: add -IL_0017: stloc V_1 -IL_001b: nop -IL_001c: nop -IL_001d: ldarg.0 -IL_001e: ldloc V_1 -IL_0022: nop -IL_0023: nop -IL_0024: call System.String TypeInferenceInDeclarations::Bar(System.Int32) -IL_0029: stloc V_2 -IL_002d: nop -IL_002e: nop -IL_002f: ldloc V_2 -IL_0033: nop -IL_0034: nop -IL_0035: call System.Void System.Console::WriteLine(System.String) -IL_003a: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Generics/CoContraVariance.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Generics/CoContraVariance.cs.txt index b464af4d..205558d1 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Generics/CoContraVariance.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Generics/CoContraVariance.cs.txt @@ -1,6 +1,8 @@ using System; +#if !API_DRIVER_SYSTEM_REFLECTION_METADATA public delegate DO Del(DI i); +#endif interface CoContraVariance { diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericMethodsFromAssembly.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericMethodsFromAssembly.cs.txt index 25618b98..fed4ae68 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericMethodsFromAssembly.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericMethodsFromAssembly.cs.txt @@ -6,8 +6,10 @@ class MethodInvocationOnGenericParameter int Find(int[] data) => Array.BinarySearch(data, 42); +#if API_DRIVER_MONO_CECIL int Find(T[] data) => Array.BinarySearch(data, default(T)); void CallPartialyClosedMethod(T t) => M2(t, 42); void M2(S s, R r) {} +#endif } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeConstraints.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeConstraints.cs.txt index dc346fe3..3d6d814d 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeConstraints.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeConstraints.cs.txt @@ -1,15 +1,18 @@ using System; -class GenericTypeConstraints - where A : class - where B : struct - where C : new() +class GenericTypeConstraints + where A : class + where B : struct + where C : new() where D : A where E : struct, IDisposable where F : IEquatable where G : IEquatable - where H : Enum + where H : Enum where I : notnull where J : unmanaged +#if COVER_DOTNET_9_FEATURES + where K : allows ref struct +#endif { } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeDefinitionWithMembers.cs.txt b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeDefinitionWithMembers.cs.txt index 691b3e14..79ecbd9a 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeDefinitionWithMembers.cs.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/Generics/GenericTypeDefinitionWithMembers.cs.txt @@ -1,14 +1,18 @@ using System; -//public delegate T TheDelegate(T t); +#if !API_DRIVER_SYSTEM_REFLECTION_METADATA && !API_DRIVER_MONO_CECIL +public delegate T TheDelegate(T t); +#endif public class GenericClass { public T theField; public T TheProperty { get; set; } public T TheMethod(T p) { return p; } - - //public delegate T TheDelegate(T t); + +#if !API_DRIVER_SYSTEM_REFLECTION_METADATA && !API_DRIVER_MONO_CECIL + public delegate T TheDelegate(T t); +#endif } public struct GenericStruct @@ -17,13 +21,16 @@ public struct GenericStruct public T TheProperty { get; set; } public T TheMethod(T p) { return p; } - //public delegate T TheDelegate(T t); +#if !API_DRIVER_SYSTEM_REFLECTION_METADATA && !API_DRIVER_MONO_CECIL + //TODO: Generic delegates are not supported yet + public delegate T TheDelegate(T t); +#endif } public interface IGenericInterface { T TheProperty { get; set; } T TheMethod(T p); - + event EventHandler TheEventT; } \ No newline at end of file diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArrays.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArrays.cs.il.srm new file mode 100644 index 00000000..63e30b65 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArrays.cs.il.srm @@ -0,0 +1,47 @@ +.locals init (ByteBuffer V_0, InterfaceBuffer V_1, StructBuffer V_2, SomeStruct V_3) +IL_0000: ldloca.s V_0 +IL_0002: initobj ByteBuffer +IL_0008: ldloca V_0 +IL_000c: call !!1& ::InlineArrayFirstElementRef(!!0&) +IL_0011: ldc.i4 1 +IL_0016: stind.i1 +IL_0017: ldloca V_0 +IL_001b: ldc.i4 1 +IL_0020: call !!1& ::InlineArrayElementRef(!!0&,System.Int32) +IL_0025: ldloca V_0 +IL_0029: call !!1& ::InlineArrayFirstElementRef(!!0&) +IL_002e: ldind.u1 +IL_002f: ldc.i4 1 +IL_0034: add +IL_0035: conv.u1 +IL_0036: stind.i1 +IL_0037: ldloca V_0 +IL_003b: ldc.i4 1 +IL_0040: call !!1& ::InlineArrayElementRef(!!0&,System.Int32) +IL_0045: ldind.u1 +IL_0046: call System.Void System.Console::WriteLine(System.Int32) +IL_004b: ldloca.s V_1 +IL_004d: initobj InterfaceBuffer +IL_0053: ldloca V_1 +IL_0057: call !!1& ::InlineArrayFirstElementRef(!!0&) +IL_005c: ldc.i4 1 +IL_0061: box System.Int32 +IL_0066: stind.ref +IL_0067: ldloca.s V_2 +IL_0069: initobj StructBuffer +IL_006f: ldloca V_2 +IL_0073: call !!1& ::InlineArrayFirstElementRef(!!0&) +IL_0078: ldloca.s V_3 +IL_007a: initobj SomeStruct +IL_0080: ldloca.s V_3 +IL_0082: dup +IL_0083: ldc.i4 42 +IL_0088: stfld System.Int32 SomeStruct::Value +IL_008d: pop +IL_008e: ldloc V_3 +IL_0092: stobj SomeStruct +IL_0097: ldloca V_2 +IL_009b: call !!1& ::InlineArrayFirstElementRef(!!0&) +IL_00a0: ldfld System.Int32 SomeStruct::Value +IL_00a5: call System.Void System.Console::WriteLine(System.Int32) +IL_00aa: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArraysRanges.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArraysRanges.cs.il.srm new file mode 100644 index 00000000..2b01e856 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/Misc/InlineArraysRanges.cs.il.srm @@ -0,0 +1,53 @@ +.locals init (Buffer`1 V_0, System.Span`1 V_1, System.Span`1 V_2, System.Int32 V_3, System.Int32 V_4) +IL_0000: ldloca.s V_0 +IL_0002: initobj Buffer`1 +IL_0008: ldloca V_0 +IL_000c: call !!1& ::InlineArrayFirstElementRef,System.String>(!!0&) +IL_0011: ldstr "zero" +IL_0016: stind.ref +IL_0017: ldloca V_0 +IL_001b: ldc.i4 1 +IL_0020: call !!1& ::InlineArrayElementRef,System.String>(!!0&,System.Int32) +IL_0025: ldstr "um, une, one" +IL_002a: stind.ref +IL_002b: ldloca V_0 +IL_002f: ldc.i4 2 +IL_0034: call !!1& ::InlineArrayElementRef,System.String>(!!0&,System.Int32) +IL_0039: ldstr "dois, deux, two" +IL_003e: stind.ref +IL_003f: ldloca V_0 +IL_0043: ldarg.1 +IL_0044: call !!1& ::InlineArrayElementRef,System.String>(!!0&,System.Int32) +IL_0049: ldstr "i" +IL_004e: stind.ref +IL_004f: ldloca.s V_0 +IL_0051: ldc.i4 10 +IL_0056: call System.Span`1 ::InlineArrayAsSpan,System.String>(!!0&,System.Int32) +IL_005b: stloc V_2 +IL_005f: ldloca V_2 +IL_0063: ldc.i4 2 +IL_0068: stloc V_3 +IL_006c: ldc.i4 5 +IL_0071: ldloc V_3 +IL_0075: sub +IL_0076: stloc V_4 +IL_007a: ldloc V_3 +IL_007e: ldloc V_4 +IL_0082: call System.Span`1 System.Span`1::Slice(System.Int32,System.Int32) +IL_0087: stloc V_1 +IL_008b: ldloca V_1 +IL_008f: ldc.i4 0 +IL_0094: call !0& System.Span`1::get_Item(System.Int32) +IL_0099: ldind.ref +IL_009a: call System.Void System.Console::WriteLine(System.String) +IL_009f: ldloca V_1 +IL_00a3: ldc.i4 1 +IL_00a8: call !0& System.Span`1::get_Item(System.Int32) +IL_00ad: ldind.ref +IL_00ae: call System.Void System.Console::WriteLine(System.String) +IL_00b3: ldloca V_1 +IL_00b7: ldarg.1 +IL_00b8: call !0& System.Span`1::get_Item(System.Int32) +IL_00bd: ldind.ref +IL_00be: call System.Void System.Console::WriteLine(System.String) +IL_00c3: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Statements/ForStatement.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Statements/ForStatement.cs.il.srm index 053b1bd2..e8adfdd1 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/Statements/ForStatement.cs.il.srm +++ b/Cecilifier.Core.Tests/TestResources/Integration/Statements/ForStatement.cs.il.srm @@ -1,38 +1,20 @@ .locals init (System.Int32 V_0, System.Int32 V_1) IL_0000: ldc.i4 0 IL_0005: stloc V_0 -IL_0009: nop -IL_000a: nop -IL_000b: ldc.i4 42 -IL_0010: stloc V_1 -IL_0014: nop -IL_0015: nop -IL_0016: ldloc V_1 -IL_001a: nop -IL_001b: nop -IL_001c: ldc.i4 10 -IL_0021: clt -IL_0023: brfalse IL_0052 -IL_0028: ldloc V_0 -IL_002c: nop -IL_002d: nop -IL_002e: ldloc V_1 -IL_0032: nop -IL_0033: nop -IL_0034: add -IL_0035: stloc V_0 -IL_0039: nop -IL_003a: nop -IL_003b: ldloc V_1 -IL_003f: nop -IL_0040: nop -IL_0041: ldc.i4 1 -IL_0046: add -IL_0047: stloc V_1 -IL_004b: nop -IL_004c: nop -IL_004d: br IL_0016 -IL_0052: ldloc V_0 -IL_0056: nop -IL_0057: nop -IL_0058: ret +IL_0009: ldc.i4 42 +IL_000e: stloc V_1 +IL_0012: ldloc V_1 +IL_0016: ldc.i4 10 +IL_001b: clt +IL_001d: brfalse IL_0042 +IL_0022: ldloc V_0 +IL_0026: ldloc V_1 +IL_002a: add +IL_002b: stloc V_0 +IL_002f: ldloc V_1 +IL_0033: ldc.i4 1 +IL_0038: add +IL_0039: stloc V_1 +IL_003d: br IL_0012 +IL_0042: ldloc V_0 +IL_0046: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Statements/SwitchStatement.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Statements/SwitchStatement.cs.il.srm new file mode 100644 index 00000000..fc5ef7b0 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/Statements/SwitchStatement.cs.il.srm @@ -0,0 +1,30 @@ +.locals init (System.Int32 V_0) +IL_0000: ldarg.1 +IL_0001: ldc.i4 2 +IL_0006: add +IL_0007: stloc V_0 +IL_000b: ldloc V_0 +IL_000f: ldc.i4 0 +IL_0014: beq.s IL_0031 +IL_0016: ldloc V_0 +IL_001a: ldc.i4 1 +IL_001f: beq.s IL_0031 +IL_0021: ldloc V_0 +IL_0025: ldc.i4 2 +IL_002a: beq.s IL_0049 +IL_002c: br IL_0050 +IL_0031: ldarg.1 +IL_0032: ldc.i4 2 +IL_0037: add +IL_0038: call System.Void System.Console::WriteLine(System.Int32) +IL_003d: ldc.i4 2 +IL_0042: starg.s 1 +IL_0044: br IL_005c +IL_0049: ldc.i4 1 +IL_004e: neg +IL_004f: ret +IL_0050: ldc.i4 3 +IL_0055: starg.s 1 +IL_0057: br IL_005c +IL_005c: ldarg.1 +IL_005d: ret diff --git a/Cecilifier.Core.Tests/TestResources/Integration/Statements/UsingStatement.StructExpression.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/Statements/UsingStatement.StructExpression.cs.il.srm new file mode 100644 index 00000000..05444123 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/Statements/UsingStatement.StructExpression.cs.il.srm @@ -0,0 +1,12 @@ +.locals init (DisposableStruct V_0, DisposableStruct V_1) +IL_0000: ldloca.s V_0 +IL_0002: initobj DisposableStruct +IL_0008: ldloc V_0 +IL_000c: stloc V_1 +IL_0010: leave IL_0025 +IL_0015: ldloca V_1 +IL_0019: constrained. DisposableStruct +IL_001f: callvirt System.Void System.IDisposable::Dispose() +IL_0024: endfinally +IL_0025: ret +.try IL_0010 to IL_0015 finally handler IL_0015 to IL_0025 diff --git a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.srm new file mode 100644 index 00000000..fe4947a9 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.srm @@ -0,0 +1,35 @@ +.locals init () +IL_0000: ldarg.0 +IL_0001: ldc.i4 1 +IL_0006: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_000b: ldarg.0 +IL_000c: ldc.i4 2 +IL_0011: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0016: ldarg.0 +IL_0017: ldc.i4 3 +IL_001c: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0021: leave IL_0043 +IL_0026: pop +IL_0027: ldarg.0 +IL_0028: ldc.i4 4 +IL_002d: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0032: leave IL_0043 +IL_0037: ldarg.0 +IL_0038: ldc.i4 5 +IL_003d: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0042: endfinally +IL_0043: leave IL_0065 +IL_0048: pop +IL_0049: ldarg.0 +IL_004a: ldc.i4 6 +IL_004f: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0054: leave IL_0065 +IL_0059: ldarg.0 +IL_005a: ldc.i4 7 +IL_005f: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0064: endfinally +IL_0065: ret +.try IL_0016 to IL_0026 catch System.Exception handler IL_0026 to IL_0037 +.try IL_0016 to IL_0037 finally handler IL_0037 to IL_0043 +.try IL_000b to IL_0048 catch System.Exception handler IL_0048 to IL_0059 +.try IL_000b to IL_0059 finally handler IL_0059 to IL_0065 diff --git a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.txt index 743760b6..31099eb6 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/NestedTryCatchFinally.cs.il.txt @@ -10,32 +10,34 @@ IL_0017: nop IL_0018: ldarg.0 IL_0019: ldc.i4 3 IL_001e: call System.Void NestedTryCatchFinally::Foo(System.Int32) -IL_0023: leave IL_0046 -IL_0028: pop -IL_0029: ldarg.0 -IL_002a: ldc.i4 4 -IL_002f: call System.Void NestedTryCatchFinally::Foo(System.Int32) -IL_0034: leave IL_0046 -IL_0039: nop -IL_003a: ldarg.0 -IL_003b: ldc.i4 5 -IL_0040: call System.Void NestedTryCatchFinally::Foo(System.Int32) -IL_0045: endfinally -IL_0046: nop -IL_0047: leave IL_006a -IL_004c: pop -IL_004d: ldarg.0 -IL_004e: ldc.i4 6 -IL_0053: call System.Void NestedTryCatchFinally::Foo(System.Int32) -IL_0058: leave IL_006a -IL_005d: nop -IL_005e: ldarg.0 -IL_005f: ldc.i4 7 -IL_0064: call System.Void NestedTryCatchFinally::Foo(System.Int32) -IL_0069: endfinally -IL_006a: nop -IL_006b: ret -.try IL_0017 to IL_0028 catch System.Exception handler IL_0028 to IL_0039 -.try IL_0017 to IL_0039 finally handler IL_0039 to IL_0046 -.try IL_000b to IL_004c catch System.Exception handler IL_004c to IL_005d -.try IL_000b to IL_005d finally handler IL_005d to IL_006a +IL_0023: leave IL_0047 +IL_0028: nop +IL_0029: pop +IL_002a: ldarg.0 +IL_002b: ldc.i4 4 +IL_0030: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0035: leave IL_0047 +IL_003a: nop +IL_003b: ldarg.0 +IL_003c: ldc.i4 5 +IL_0041: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_0046: endfinally +IL_0047: nop +IL_0048: leave IL_006c +IL_004d: nop +IL_004e: pop +IL_004f: ldarg.0 +IL_0050: ldc.i4 6 +IL_0055: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_005a: leave IL_006c +IL_005f: nop +IL_0060: ldarg.0 +IL_0061: ldc.i4 7 +IL_0066: call System.Void NestedTryCatchFinally::Foo(System.Int32) +IL_006b: endfinally +IL_006c: nop +IL_006d: ret +.try IL_0017 to IL_0028 catch System.Exception handler IL_0028 to IL_003a +.try IL_0017 to IL_003a finally handler IL_003a to IL_0047 +.try IL_000b to IL_004d catch System.Exception handler IL_004d to IL_005f +.try IL_000b to IL_005f finally handler IL_005f to IL_006c diff --git a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.srm b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.srm new file mode 100644 index 00000000..a81ea242 --- /dev/null +++ b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.srm @@ -0,0 +1,17 @@ +.locals init () +IL_0000: ldarg.0 +IL_0001: ldc.i4 1 +IL_0006: call System.Void TryCatchFinally::Foo(System.Int32) +IL_000b: leave IL_002d +IL_0010: pop +IL_0011: ldarg.0 +IL_0012: ldc.i4 2 +IL_0017: call System.Void TryCatchFinally::Foo(System.Int32) +IL_001c: leave IL_002d +IL_0021: ldarg.0 +IL_0022: ldc.i4 3 +IL_0027: call System.Void TryCatchFinally::Foo(System.Int32) +IL_002c: endfinally +IL_002d: ret +.try IL_0000 to IL_0010 catch System.Exception handler IL_0010 to IL_0021 +.try IL_0000 to IL_0021 finally handler IL_0021 to IL_002d diff --git a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.txt b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.txt index 28f59c6a..d58aa2b8 100644 --- a/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.txt +++ b/Cecilifier.Core.Tests/TestResources/Integration/TryCatchFinally/TryCatchFinally.cs.il.txt @@ -3,18 +3,19 @@ IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4 1 IL_0007: call System.Void TryCatchFinally::Foo(System.Int32) -IL_000c: leave IL_002f -IL_0011: pop -IL_0012: ldarg.0 -IL_0013: ldc.i4 2 -IL_0018: call System.Void TryCatchFinally::Foo(System.Int32) -IL_001d: leave IL_002f -IL_0022: nop -IL_0023: ldarg.0 -IL_0024: ldc.i4 3 -IL_0029: call System.Void TryCatchFinally::Foo(System.Int32) -IL_002e: endfinally -IL_002f: nop -IL_0030: ret -.try IL_0000 to IL_0011 catch System.Exception handler IL_0011 to IL_0022 -.try IL_0000 to IL_0022 finally handler IL_0022 to IL_002f +IL_000c: leave IL_0030 +IL_0011: nop +IL_0012: pop +IL_0013: ldarg.0 +IL_0014: ldc.i4 2 +IL_0019: call System.Void TryCatchFinally::Foo(System.Int32) +IL_001e: leave IL_0030 +IL_0023: nop +IL_0024: ldarg.0 +IL_0025: ldc.i4 3 +IL_002a: call System.Void TryCatchFinally::Foo(System.Int32) +IL_002f: endfinally +IL_0030: nop +IL_0031: ret +.try IL_0000 to IL_0011 catch System.Exception handler IL_0011 to IL_0023 +.try IL_0000 to IL_0023 finally handler IL_0023 to IL_0030 diff --git a/Cecilifier.Core.Tests/Tests/Integration/Expressions/Casts/CastsTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/Expressions/Casts/CastsTestCase.cs index bef548aa..0d7ccc55 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Expressions/Casts/CastsTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Expressions/Casts/CastsTestCase.cs @@ -37,7 +37,6 @@ public void TestGenericTypeParameter([Values("object", "T")] string source, [Val } [Test] - [ParameterizedResourceFilter(IgnoreReason = "Generic types are not supported as of today on SRM")] public void TestGenerics([Values("Base", "Derived")] string source, [Values("Base", "Derived")] string target) { AssertResourceTestWithParameters("Expressions/Casts/GenericTypeCasts", source, target); diff --git a/Cecilifier.Core.Tests/Tests/Integration/Expressions/ExpressionTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/Expressions/ExpressionTestCase.cs index 2f1187f3..2642fb5d 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Expressions/ExpressionTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Expressions/ExpressionTestCase.cs @@ -9,31 +9,6 @@ namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] - [EnableForContext( - nameof(TestAdd), - nameof(TestAdd2), - nameof(TestBox), - nameof(TestDoubleLocalVariableInitialization), - nameof(TestLocalVariableAssignment), - nameof(TestLocalVariableInitialization), - nameof(TestTimes), - nameof(TestParameterAssignment), - nameof(TestMultipleLocalVariableAssignment), - nameof(TestDoubleLocalVariableInitializationComplex), - nameof(TestModulus), - nameof(TestEquals), - nameof(TestLessThan), - nameof(TestTernaryOperator), - nameof(TestTypeInferenceInDeclarations), - nameof(TestValueTypeAddress), - nameof(TestNewPrimitive), - nameof(TestNewCustom), - nameof(TestNewSingleDimensionArray), - nameof(TestUnaryExpressions), - nameof(TestIncrementDecrementExpressions), - nameof(TestIndexerAccess), - nameof(TestRangeExpression), - nameof(TestIndexExpression))] public class ExpressionTestCase : ResourceTestBase where TResource : IVisitorContext { [Test] @@ -169,13 +144,13 @@ public void TestIncrementDecrementExpressions([Values("Pre", "Post")] string kin [TestCase("PropertyRead")] [TestCase("ArrayWrite")] [TestCase("PropertyWrite")] - [ParameterizedResourceFilter("ArrayRead", "ArrayWrite", IgnoreReason = "Other tests depends on generics which are not supported as of today")] public void TestIndexerAccess(string prefix) { AssertResourceTest($"Expressions/{prefix}IndexerAccess"); } [Test] + [DisableForContext(IgnoreReason = "SRM throws exception complaining about generic type parameters not being sorted. #111")] public void TestArrayLength() { AssertResourceTest("Expressions/ArrayLength"); diff --git a/Cecilifier.Core.Tests/Tests/Integration/Expressions/LambdaExpressionTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/Expressions/LambdaExpressionTestCase.cs index 07ae4b4c..171fecad 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Expressions/LambdaExpressionTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Expressions/LambdaExpressionTestCase.cs @@ -1,11 +1,15 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; +using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration; [TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] +[EnableForContext(IgnoreReason = "Not Supported")] public class LambdaExpressionTestCase : ResourceTestBase where TResource : IVisitorContext { [Test] diff --git a/Cecilifier.Core.Tests/Tests/Integration/Expressions/StackallocTests.cs b/Cecilifier.Core.Tests/Tests/Integration/Expressions/StackallocTests.cs index 5f6e2ccf..d7ea38d1 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Expressions/StackallocTests.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Expressions/StackallocTests.cs @@ -1,11 +1,14 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; +using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration; [TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] public class StackallocTests : ResourceTestBase where TResource : IVisitorContext { [TestCase("simplest", TestName = "Simplest")] @@ -13,6 +16,7 @@ public class StackallocTests : ResourceTestBase where TRes [TestCase("WithSpanAsParameter", true, TestName = "WithSpanAsParameter")] [TestCase("WithInitializer", true, TestName = "WithInitializer")] [TestCase("CustomValueType", TestName = "CustomValueType")] + [ParameterizedResourceFilter("simplest")] public void TestStackalloc(string testFile, bool hasExplicitExpectations = false) { var options = new CecilifyTestOptions() diff --git a/Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs index 8bb29dbb..cfec917a 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs @@ -9,13 +9,6 @@ namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] - [EnableForContext( - nameof(TestGenericOuterAndInnerPermutations), - nameof(TestSimplestGenericTypeDefinition), - nameof(TestGenericTypesAsMembers), - nameof(TestGenericTypeInstantiation), - nameof(TestGenericTypesInheritance), - IgnoreReason = "Not implemented")] public class GenericsTestCase : ResourceTestBase where TResource : IVisitorContext { [TestCase("GenericOuterNonGenericInner")] @@ -33,7 +26,7 @@ public void TestGenericOuterAndInnerPermutations(string testName) [Test] public void TestInstanceNonGenericMethodsOnGenericTypes() { - AssertResourceTest(@"Generics/InstanceNonGenericMethodsOnGenericTypes"); + AssertResourceTest("Generics/InstanceNonGenericMethodsOnGenericTypes"); } [Test] @@ -103,13 +96,13 @@ public void TestGenericMethods(string testName) [Test] public void TestGenericMethodConstraints() { - AssertResourceTest(@"Generics/GenericMethodConstraints"); + AssertResourceTest("Generics/GenericMethodConstraints"); } [Test] public void TestGenericTypeConstraints() { - AssertResourceTest(@"Generics/GenericTypeConstraints"); + AssertResourceTest("Generics/GenericTypeConstraints"); } [Test] @@ -121,7 +114,7 @@ public void TestGenericTypeUsedAsConstraint() [Test] public void TestCoContraVariance() { - AssertResourceTest(@"Generics/CoContraVariance"); + AssertResourceTest("Generics/CoContraVariance"); } [Test] diff --git a/Cecilifier.Core.Tests/Tests/Integration/MembersTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/MembersTestCase.cs index 5fae8d1d..c62a88c1 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/MembersTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/MembersTestCase.cs @@ -1,11 +1,14 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; +using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] + [TestFixture(typeof(SystemReflectionMetadataContext))] public class MembersTestCase : ResourceTestBase where TResource : IVisitorContext { [Test] @@ -17,6 +20,7 @@ public void TestForwardReferences() [TestCase("SimpleEvent")] [TestCase("StaticEvent")] [TestCase("GenericEventHandler")] + [ParameterizedResourceFilter("SimpleEvent", "StaticEvent")] public void TestEvents(string testName) { AssertResourceTest($"Members/Events/{testName}"); diff --git a/Cecilifier.Core.Tests/Tests/Integration/MethodTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/MethodTestCase.cs index 1a7d27a2..d569e266 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/MethodTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/MethodTestCase.cs @@ -2,30 +2,12 @@ using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; -using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] - [EnableForContext(nameof(NonVirtualMethodCallTest), - nameof(TestCtorWithParameters), - nameof(TestDefaultCtorFromBaseClass), - nameof(TestExplicityDefaultCtor), - nameof(TestExternalMethodReference), - nameof(TestInterfaceMethodVirtualImplementation), - nameof(TestMethodCallOnValueType), - nameof(TestMethodInvocation), - nameof(TestMultipleParameters), - nameof(TestMutuallyRecursive), - nameof(TestNoParameters), - nameof(TestRefReturn), - nameof(TestReturnValue), - nameof(TestSelfReferencingCtor), - nameof(TestSingleSimpleParameter), - nameof(TestTypeWithNoArgCtorAndInnerClass), - nameof(TestVariableNumberOfParameters))] public class MethodTestCase : ResourceTestBase where TResource : IVisitorContext { [Test] @@ -47,7 +29,7 @@ public void TestDefaultCtorFromBaseClass() } [Test] - public void TestExplicityDefaultCtor() + public void TestExplicitDefaultCtor() { AssertResourceTest("Members/Methods/ExplicityDefaultCtor"); } @@ -164,7 +146,6 @@ public void TestDelegateAsParameter(string implicitOrExplicit) [TestCase("ArrayParam")] [TestCase("ParamIndexer")] [TestCase("RefReturnField")] - [ParameterizedResourceFilter("ArrayParam", "RefReturnField")] public void TestRefReturn(string test, string ignoredErrors = null) { var options = new CecilifyTestOptions { ResourceName = $"Members/Methods/{test}", IgnoredILErrors = ignoredErrors }; diff --git a/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/InlineArraysTests.cs b/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/InlineArraysTests.cs index 32ee31e9..c2eb850f 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/InlineArraysTests.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/InlineArraysTests.cs @@ -1,4 +1,5 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; using NUnit.Framework; @@ -6,11 +7,14 @@ namespace Cecilifier.Core.Tests.Integration; [TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] public class InlineArraysTests : ResourceTestBase where TResource : IVisitorContext { [Test] public void TestInlineArrays() { + // The assembly emitted from C# compiler differs basically in: 1) order of type/member declarations, 2) use of short version of some IL instructions and some minor IL differences + // Verified by hand that the generated IL is equivalent. AssertResourceTestWithExplicitExpectation("Misc/InlineArrays", "System.Void InlineArrayTests::Test()"); } diff --git a/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/MiscTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/MiscTestCase.cs index 354181c0..88770cbc 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/MiscTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Miscelaneous/MiscTestCase.cs @@ -10,12 +10,14 @@ namespace Cecilifier.Core.Tests.Integration [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] [EnableForContext( - nameof(TestTopLevelStatements), - nameof(TestNamespaces), + nameof(TestDelegateInvocation), + nameof(TestAccessibilityModifiers), nameof(TestNamespaces), nameof(AttributesOnMembers), nameof(TestAttributesOnExplicitTargets), nameof(TestAttributeWithArrayInitializer), + nameof(TestDllImport), + nameof(TestTopLevelStatements), IgnoreReason = "Not implemented yet")] public class MiscTestCase : ResourceTestBase where TResource : IVisitorContext { @@ -23,6 +25,7 @@ public class MiscTestCase : ResourceTestBase where TResour [TestCase("Parameters2")] [TestCase("LocalVariables")] [TestCase("OnFields")] + [ParameterizedResourceFilter("Parameters2")] public void TestDelegateInvocation(string storageType) { AssertResourceTest($"Misc/DelegateInvocation_{storageType}"); @@ -82,7 +85,7 @@ public void TestFunctionPointer(string testName) [TestCase("InterfaceAndMembers")] [TestCase("EnumAndMembers")] [TestCase("StructAndMembers")] - [ParameterizedResourceFilter("StructAndMembers", "EnumAndMembers")] + [ParameterizedResourceFilter("ClassAndMembers", "InterfaceAndMembers", "StructAndMembers", "EnumAndMembers")] public void AttributesOnMembers(string typeKind) { AssertResourceTest($"Misc/Attributes/AttributesOn{typeKind}"); @@ -103,7 +106,7 @@ public void TestAttributeWithArrayInitializer() [Test] public void TestDllImport() { - AssertResourceTest($@"Misc/Attributes/DllImportAttribute"); + AssertResourceTest("Misc/Attributes/DllImportAttribute"); } [TestCase("TopLevelStatementsExplicitReturn")] diff --git a/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs b/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs index 1c468e5e..91f25a37 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/StatementTests.cs @@ -2,14 +2,12 @@ using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; -using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] - [EnableForContext(nameof(TestForStatement))] public class StatementTests : ResourceTestBase where TResource : IVisitorContext { [TestCase("System.Int32* FixedStatementTest::Test()", TestName = "Return")] @@ -34,19 +32,19 @@ public void TestForStatement() [Test] public void TestSwitchStatement() { - AssertResourceTestWithExplicitExpectation(@"Statements/SwitchStatement", "System.Int32 SwitchStatement::M(System.Int32)"); + AssertResourceTestWithExplicitExpectation("Statements/SwitchStatement", "System.Int32 SwitchStatement::M(System.Int32)"); } [Test] public void TestUsingWithStructExpression() { - AssertResourceTestWithExplicitExpectation(@"Statements/UsingStatement.StructExpression", "System.Void UsingStatementTest::WithStructExpression()"); + AssertResourceTestWithExplicitExpectation("Statements/UsingStatement.StructExpression", "System.Void UsingStatementTest::WithStructExpression()"); } [Test] public void TestUsingStatement() { - AssertResourceTest(@"Statements/UsingStatement"); + AssertResourceTest("Statements/UsingStatement"); } } } diff --git a/Cecilifier.Core.Tests/Tests/Integration/TryCatchFinallyTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/TryCatchFinallyTestCase.cs index c9487f8b..eeee27d7 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/TryCatchFinallyTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/TryCatchFinallyTestCase.cs @@ -1,4 +1,5 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; using NUnit.Framework; @@ -6,6 +7,7 @@ namespace Cecilifier.Core.Tests.Integration { [TestFixture(typeof(MonoCecilContext))] + [TestFixture(typeof(SystemReflectionMetadataContext))] public class TryCatchFinallyTestCase : ResourceTestBase where TResource : IVisitorContext { [TestCase("TryCatch")] diff --git a/Cecilifier.Core.Tests/Tests/Integration/Types/DelegatesTests.cs b/Cecilifier.Core.Tests/Tests/Integration/Types/DelegatesTests.cs index 4b4cd8cb..c1b1ffdd 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Types/DelegatesTests.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Types/DelegatesTests.cs @@ -1,11 +1,15 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; +using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration.Types { [TestFixture(typeof(MonoCecilContext))] + [TestFixture(typeof(SystemReflectionMetadataContext))] + [EnableForContext(IgnoreReason = "Not Supported")] public class DelegatesTests : ResourceTestBase where TResource : IVisitorContext { [TestCase("CustomDelegateMultipleParameters")] diff --git a/Cecilifier.Core.Tests/Tests/Integration/Types/TypesTestCase.cs b/Cecilifier.Core.Tests/Tests/Integration/Types/TypesTestCase.cs index d863cd14..21edf824 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/Types/TypesTestCase.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/Types/TypesTestCase.cs @@ -5,133 +5,116 @@ using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; -namespace Cecilifier.Core.Tests.Integration.Types +namespace Cecilifier.Core.Tests.Integration.Types; + +[TestFixture(typeof(MonoCecilContext), TestName = "Mono.Cecil")] +[TestFixture(typeof(SystemReflectionMetadataContext), TestName = "SRM")] +public class TypesTestCase : ResourceTestBase where TContext : IVisitorContext { - [TestFixture(typeof(MonoCecilContext), TestName = "Mono.Cecil")] - [TestFixture(typeof(SystemReflectionMetadataContext), TestName = "SRM")] - [EnableForContext( - nameof(ForwardTypeReferenceTest), - nameof(InheritanceSameCompilationUnitTest), - nameof(InheritanceTest), - nameof(InnerClassTest), - nameof(InterfaceDefinitionTest), - nameof(InterfaceImplementationTest), - nameof(InterfaceWithPropertiesTest), - nameof(PartialClassTest), - nameof(ReadOnlyStructTest), - nameof(SealedClassTest), - nameof(SimplestTest), - nameof(SimpleValueTypeTest), - nameof(TypeInitializeTest), - nameof(AbstractClassTest), - IgnoreReason = "Not implemented")] - - public class TypesTestCase : ResourceTestBase where TContext : IVisitorContext - { - [TestCase("SimpleTypeWithAttribute")] - [TestCase("AttributeWithProperty")] - [TestCase("AttributeFromSameAssembly")] - [TestCase("AttributeWithTypeOfExpression")] - [TestCase("AttributeGeneric")] - public void AttributeTests(string typeName) - { - AssertResourceTest($"Types/{typeName}"); - } - - [Test] - public void AbstractClassTest() - { - AssertResourceTest("Types/AbstractClass"); - } - - [Test] - public void ExplicitInterfaceImplementationTest() - { - AssertResourceTest("Types/ExplicitInterfaceImplementation"); - } - - [Test] - public void ForwardTypeReferenceTest() - { - AssertResourceTest("Types/ForwardTypeReference"); - } - - [Test] - public void InheritanceSameCompilationUnitTest() - { - AssertResourceTest("Types/InheritanceSameCompilationUnit"); - } - - [Test] - public void InheritanceTest() - { - AssertResourceTest("Types/Inheritance"); - } - - [Test] - public void InnerClassTest() - { - AssertResourceTest("Types/InnerClass"); - } - - [Test] - public void InterfaceDefinitionTest() - { - AssertResourceTest("Types/InterfaceDefinition"); - } - - [Test] - public void InterfaceWithPropertiesTest() - { - AssertResourceTest("Types/InterfaceWithProperties"); - } - - [Test] - public void InterfaceImplementationTest() - { - AssertResourceTest("Types/InterfaceImplementation"); - } - - [Test] - public void MultipleInterfaceImplementationTest() - { - AssertResourceTest("Types/MultipleInterfaceImplementation"); - } - - [Test] - public void PartialClassTest() - { - AssertResourceTest("Types/PartialClass"); - } - - [Test] - public void SealedClassTest() - { - AssertResourceTest("Types/SealedClass"); - } - - [Test] - public void SimplestTest() - { - AssertResourceTest("Types/Simplest"); - } - - [Test] - public void SimpleValueTypeTest() - { - AssertResourceTest("Types/SimpleValueType"); - } - - [Test] - public void TypeInitializeTest() - { - AssertResourceTest("Types/TypeInitializer"); - } - - [Test] - public void ReadOnlyStructTest() - { - AssertResourceTest("Types/ReadOnlyStruct"); - } + [TestCase("SimpleTypeWithAttribute")] + [TestCase("AttributeWithProperty")] + [TestCase("AttributeFromSameAssembly")] + [TestCase("AttributeWithTypeOfExpression")] + [TestCase("AttributeGeneric")] + [ParameterizedResourceFilter("AttributeWithProperty", IgnoreReason = "Other tests are failing")] + public void AttributeTests(string typeName) + { + AssertResourceTest($"Types/{typeName}"); + } + + [Test] + public void AbstractClassTest() + { + AssertResourceTest("Types/AbstractClass"); + } + + [Test] + public void ExplicitInterfaceImplementationTest() + { + AssertResourceTest("Types/ExplicitInterfaceImplementation"); + } + + [Test] + public void ForwardTypeReferenceTest() + { + AssertResourceTest("Types/ForwardTypeReference"); + } + + [Test] + public void InheritanceSameCompilationUnitTest() + { + AssertResourceTest("Types/InheritanceSameCompilationUnit"); + } + + [Test] + public void InheritanceTest() + { + AssertResourceTest("Types/Inheritance"); + } + + [Test] + public void InnerClassTest() + { + AssertResourceTest("Types/InnerClass"); + } + + [Test] + public void InterfaceDefinitionTest() + { + AssertResourceTest("Types/InterfaceDefinition"); + } + + [Test] + public void InterfaceWithPropertiesTest() + { + AssertResourceTest("Types/InterfaceWithProperties"); + } + + [Test] + public void InterfaceImplementationTest() + { + AssertResourceTest("Types/InterfaceImplementation"); + } + + [Test] + public void MultipleInterfaceImplementationTest() + { + AssertResourceTest("Types/MultipleInterfaceImplementation"); + } + + [Test] + public void PartialClassTest() + { + AssertResourceTest("Types/PartialClass"); + } + + [Test] + public void SealedClassTest() + { + AssertResourceTest("Types/SealedClass"); + } + + [Test] + public void SimplestTest() + { + AssertResourceTest("Types/Simplest"); + } + + [Test] + public void SimpleValueTypeTest() + { + AssertResourceTest("Types/SimpleValueType"); + } + + [Test] + public void TypeInitializeTest() + { + AssertResourceTest("Types/TypeInitializer"); + } + + [Test] + public void ReadOnlyStructTest() + { + AssertResourceTest("Types/ReadOnlyStruct"); } } diff --git a/Cecilifier.Core.Tests/Tests/Integration/ValueTypesTests.cs b/Cecilifier.Core.Tests/Tests/Integration/ValueTypesTests.cs index f450e66a..fcfff005 100644 --- a/Cecilifier.Core.Tests/Tests/Integration/ValueTypesTests.cs +++ b/Cecilifier.Core.Tests/Tests/Integration/ValueTypesTests.cs @@ -2,7 +2,6 @@ using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; -using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.Integration @@ -18,13 +17,6 @@ public class ValueTypesTests : ResourceTestBase where TRes [TestCase("SingleBoolLiteralAsTargetOfCall")] [TestCase("ValueTypeReturnAsTargetOfCallInsideBaseConstructorInvocation")] [TestCase("ValueTypeReturnAsTargetOfCallInsideConstructor")] - [ParameterizedResourceFilter( - "MultipleLiteralAsTargetOfCall", - "SingleLiteralAsTargetOfCall", - "SingleDoubleLiteralAsTargetOfCall", - "SingleBoolLiteralAsTargetOfCall", - "ValueTypeReturnAsTargetOfCallInsideBaseConstructorInvocation", - "ValueTypeReturnAsTargetOfCallInsideConstructor")] public void ValueTypeAsTargetOfCall(string testResourceBaseName) { AssertResourceTest(new CecilifyTestOptions { ResourceName = $"ValueTypes/AsTargetOfCall/{testResourceBaseName}" }); diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/CollectionExpressionTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/CollectionExpressionTests.cs index 3f9cbd22..f1f14393 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/CollectionExpressionTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/CollectionExpressionTests.cs @@ -12,7 +12,7 @@ namespace Cecilifier.Core.Tests.OutputBased; [TestFixture(typeof(MonoCecilContext), TestName = "Mono.Cecil")] [TestFixture(typeof(SystemReflectionMetadataContext), TestName = "System.Reflection.Metadata")] -[EnableForContext(IgnoreReason = "Not implemented yet")] +[EnableForContext("ArrayWith3OrMoreElements", "ArrayWith2OrMoreElements", IgnoreReason = "Not implemented yet")] public class CollectionExpressionTests : OutputBasedTestBase where TContext : IVisitorContext { [Test] @@ -86,6 +86,7 @@ class Bar } [Test] + [ParameterizedResourceFilter("long[]")] public void ImplicitNumericConversions_Are_Applied([Values("List", "long[]", "Span")] string targetType, [Values("[2, 1]", "[5, 4, 3, 2, 1]")] string items) { AssertOutput( @@ -108,6 +109,7 @@ public void ImplicitUserDefinedConversions_Are_Applied([Values("List", "Foo } [Test] + [ParameterizedResourceFilter("object[]")] public void BoxConversions_Are_Applied([Values("List", "object[]", "Span")] string targetType, [Values("[2, 1]", "[5, 4, 3, 2, 1]")] string items) { AssertConversionIsApplied(targetType, items, "object"); diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/EnumTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/EnumTests.cs index 884c1a30..c5e0c02f 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/EnumTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/EnumTests.cs @@ -2,7 +2,6 @@ using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; -using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.OutputBased; diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/FieldTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/FieldTests.cs index 5cce992c..784d6888 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/FieldTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/FieldTests.cs @@ -9,11 +9,11 @@ namespace Cecilifier.Core.Tests.OutputBased; [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] -[EnableForContext(nameof(Simple), IgnoreReason = "Not implemented yet")] public class FieldTests : OutputBasedTestBase where TContext : IVisitorContext { [TestCase("int", "field = 42", "42", TestName = "Non generic")] [TestCase("T", "field = true", "True", TestName = "Generic")] + [ParameterizedResourceFilter("int")] public void InstanceFieldOnGenericType(string fieldType, string fieldInitialization, string expectedOutput) { AssertOutput($$""" @@ -27,6 +27,7 @@ class Foo { public {{fieldType}} field; } [TestCase("int", "42", "42", TestName = "Non generic")] [TestCase("T", "true", "True", TestName = "Generic")] + [ParameterizedResourceFilter("int")] public void StaticFieldOnGenericType(string fieldType, string fieldValue, string expectedOutput) { AssertOutput($$""" diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/GenericTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/GenericTests.cs index aabc1726..80a59a77 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/GenericTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/GenericTests.cs @@ -9,7 +9,6 @@ namespace Cecilifier.Core.Tests.OutputBased; [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] -[EnableForContext(IgnoreReason = "Not implemented yet")] public class GenericTests : OutputBasedTestBase where TContext : IVisitorContext { [Test] @@ -53,4 +52,27 @@ public void GenericInstanceMethod_ReferencingTypeParametersFromDeclaringType_Wor "123"); } + [Test] + [DisableForContext(IgnoreReason = "Throws `Metadata table GenericParam not sorted.` (#375)")] + public void TypeParameterMetadataTables_AreSorted() + { + AssertOutput(""" + using System; + using System.Collections.Generic; + + Console.Write($"{TestSubject.M1(42)} {TestSubject.M2(42)}"); + + class TestSubject + { + // This fails + public static int M1(int v) => v; + public static T M2(T v) => v; + + // This works! :/ + //public static T M2(T v) => v; + //public static int M1(int v) => v; + } + """, + "42 42"); + } } diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/InvocationTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/InvocationTests.cs index 01208b70..f850dc56 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/InvocationTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/InvocationTests.cs @@ -1,11 +1,15 @@ using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; +using Cecilifier.Core.Tests.Framework.Attributes; using NUnit.Framework; namespace Cecilifier.Core.Tests.OutputBased; [TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] +[EnableForContext("ImplicitDelegateInvocation", "ExplicitDelegateInvocation")] public class InvocationTests : OutputBasedTestBase where TContext : IVisitorContext { [Test] diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/NullCoalescingOperatorTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/NullCoalescingOperatorTests.cs index 96fd4ae6..d26633cd 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/NullCoalescingOperatorTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/NullCoalescingOperatorTests.cs @@ -1,6 +1,5 @@ using Cecilifier.ApiDriver.MonoCecil; using Cecilifier.ApiDriver.SystemReflectionMetadata; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; using Cecilifier.Core.Tests.Framework.Attributes; @@ -10,7 +9,11 @@ namespace Cecilifier.Core.Tests.OutputBased; [TestFixture(typeof(MonoCecilContext))] [TestFixture(typeof(SystemReflectionMetadataContext))] -[EnableForContext(IgnoreReason = "Not implemented yet")] +[EnableForContext( + nameof(SimpleReferenceType), + nameof(Convoluted), + nameof(TestAssociative), + IgnoreReason = "Not implemented yet")] public class NullCoalescingOperatorTests : OutputBasedTestBase where TContext : IVisitorContext { [Test] diff --git a/Cecilifier.Core.Tests/Tests/OutputBased/RecordTests.cs b/Cecilifier.Core.Tests/Tests/OutputBased/RecordTests.cs index 54799105..c4bdcb8b 100644 --- a/Cecilifier.Core.Tests/Tests/OutputBased/RecordTests.cs +++ b/Cecilifier.Core.Tests/Tests/OutputBased/RecordTests.cs @@ -1,6 +1,5 @@ using Cecilifier.ApiDriver.MonoCecil; using Cecilifier.ApiDriver.SystemReflectionMetadata; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.AST; using Cecilifier.Core.Tests.Framework; using Cecilifier.Core.Tests.Framework.Attributes; diff --git a/Cecilifier.Core.Tests/Tests/Unit/ApiDriver/DelayedDefinitionsManagerTests.Methods.cs b/Cecilifier.Core.Tests/Tests/Unit/ApiDriver/DelayedDefinitionsManagerTests.Methods.cs index 620f13a2..8618eab3 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/ApiDriver/DelayedDefinitionsManagerTests.Methods.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/ApiDriver/DelayedDefinitionsManagerTests.Methods.cs @@ -34,7 +34,7 @@ public void SingleTypeWithSingleMethod() var context = NewContext(); context.DelayedDefinitionsManager.RegisterTypeDefinition("T1V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", (ctx, tdr) => "T1M1"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", "M1V", (ctx, tdr) => "T1M1"); context.DelayedDefinitionsManager.ProcessDefinitions(context); @@ -48,8 +48,8 @@ public void SingleTypeWithTwoMethods() var context = NewContext(); context.DelayedDefinitionsManager.RegisterTypeDefinition("T1V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", (ctx, tdr) => "T1M1"); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", (ctx, tdr) => "T1M2"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", "M1V", (ctx, tdr) => "T1M1"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", "M2V",(ctx, tdr) => "T1M2"); context.DelayedDefinitionsManager.ProcessDefinitions(context); @@ -65,7 +65,7 @@ public void TwoTypes_MethodOnlyInOne(string declaringTypeName) context.DelayedDefinitionsManager.RegisterTypeDefinition("T1V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T2V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition(declaringTypeName, (ctx, tdr) => "TheMethod"); + context.DelayedDefinitionsManager.RegisterMethodDefinition(declaringTypeName, "M1V",(ctx, tdr) => "TheMethod"); context.DelayedDefinitionsManager.ProcessDefinitions(context); @@ -86,8 +86,8 @@ public void TwoTypes_MethodsInBoth() context.DelayedDefinitionsManager.RegisterTypeDefinition("T1V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T2V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", (ctx, tdr) => "T1M"); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", (ctx, tdr) => "T2M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T1V", "M1V", (ctx, tdr) => "T1M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", "M2V", (ctx, tdr) => "T2M"); context.DelayedDefinitionsManager.ProcessDefinitions(context); @@ -104,8 +104,8 @@ public void TreeTypes_MethodsInLastTwo() context.DelayedDefinitionsManager.RegisterTypeDefinition("T1V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T2V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T3V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", (ctx, tdr) => "T2M"); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T3V", (ctx, tdr) => "T3M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", "M1V", (ctx, tdr) => "T2M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T3V", "M2V", (ctx, tdr) => "T3M"); context.DelayedDefinitionsManager.ProcessDefinitions(context); @@ -125,8 +125,8 @@ public void FourTypes_MethodsInSecondAndLastOnes() context.DelayedDefinitionsManager.RegisterTypeDefinition("T2V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T3V", testContext.OnTypeRegistration); context.DelayedDefinitionsManager.RegisterTypeDefinition("T4V", testContext.OnTypeRegistration); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", (ctx, tdr) => "T2M"); - context.DelayedDefinitionsManager.RegisterMethodDefinition("T4V", (ctx, tdr) => "T4M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T2V", "M1V", (ctx, tdr) => "T2M"); + context.DelayedDefinitionsManager.RegisterMethodDefinition("T4V", "M2V", (ctx, tdr) => "T4M"); context.DelayedDefinitionsManager.ProcessDefinitions(context); diff --git a/Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs b/Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs index 4e8b4cd1..d2210b48 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs @@ -109,9 +109,9 @@ void M() {} \s+var (?m_M_\d+) = new MethodDefinition\("M",.+\); \s+cls_foo_\d+.Methods.Add\(\k\); \s+m_M_\d+.Body.InitLocals = true; - \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+var (?attr_myGenericAttribute_\d+) = new CustomAttribute\(.+(?ctor_myGenericAttribute_\d+).Name, \k.ReturnType\) {.+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\), .+\); \s+\k.CustomAttributes.Add\(\k\); + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); """)); } diff --git a/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs b/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs index e98c0f83..db86e27f 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs @@ -58,6 +58,7 @@ public class DefaultExpressions : CecilifierUnitTestBase private const string DefaultTypeParameterExpectation = """ \s+m_M_\d+.Body.Variables.Add\((l_T_\d+)\); + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+il_M_\d+.Emit\(OpCodes.Ldloca_S, \1\); \s+il_M_\d+.Emit\(OpCodes.Initobj, gp_T_\d+\); \s+il_M_\d+.Emit\(OpCodes.Ldloc, \1\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/ExternalMembersTests.cs b/Cecilifier.Core.Tests/Tests/Unit/ExternalMembersTests.cs index 1b76b6f3..e885d0cb 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/ExternalMembersTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/ExternalMembersTests.cs @@ -118,7 +118,7 @@ public void Multiple_DllImports_ToDifferentModules_DoesNotCreate_Multiple_Module var result = RunCecilifier("using System.Runtime.InteropServices; public class C { [DllImport(\"Bar\")] public static extern int M(); [DllImport(\"Bar2\")] public static extern void M2(); }"); var cecilifiedCode = result.GeneratedCode.ReadToEnd(); - var matches = Regex.Matches(cecilifiedCode, "new PInvokeInfo\\(.+, \".?\", (?.+)\\);"); + var matches = Regex.Matches(cecilifiedCode, "new PInvokeInfo\\(.+,\\s+\".?\", (?.+)\\);"); Assert.That(matches.Count, Is.EqualTo(2), matches.Aggregate("Actual:", (acc, curr) => acc + (acc[^1] == ':' ? " " : " ,") + curr.Groups["targetModule"].Value)); } } diff --git a/Cecilifier.Core.Tests/Tests/Unit/ForEachStatementTests.cs b/Cecilifier.Core.Tests/Tests/Unit/ForEachStatementTests.cs index f4541ece..e970085a 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/ForEachStatementTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/ForEachStatementTests.cs @@ -92,7 +92,7 @@ public void M(System.Collections.Generic.List e) Assert.That(cecilifiedCode, Does.Match(""" //variable to store the returned 'IEnumerator'. \s+il_M_\d+.Emit\(OpCodes.Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Collections.Generic.List\), "GetEnumerator",.+\)\)\); - \s+var l_enumerator_\d+ = new VariableDefinition\(.+ImportReference\(typeof\(System.Collections.Generic.List.Enumerator\)\)\); + \s+var l_enumerator_\d+ = new VariableDefinition\(.+NewRawNestedTypeReference\("Enumerator",.+, .+ImportReference\(typeof\(.+List<>\)\), isValueType: true, 1\).MakeGenericInstanceType\(.+Int32\)\); """)); Assert.That(cecilifiedCode, Does.Match(""" @@ -135,12 +135,12 @@ public void Array() Assert.That( cecilifiedCode, Does.Match(""" - (il_topLevelMain_\d+\.)Append\(nop_\d+\); + (il_topLevelMain_\d+\.)Append\(lbl_conditionCheckLabel_\d+\); (\s+\1Emit\(OpCodes\.)Ldloc, l_index_\d+\); \2Ldloc, l_array_\d+\); \2Ldlen\); \2Conv_I4\); - \2Blt, ldloc_\d+\); + \2Blt, lbl_firstLoopBodyInstructionLabel_\d+\); """), "Array loop index check code does not match"); @@ -165,8 +165,8 @@ public void Dispose() {} var cecilifiedCode = result.GeneratedCode.ReadToEnd(); Assert.That(cecilifiedCode, Does.Match(""" //finally start - \s+var nop_\d+ = (?il_topLevelMain_\d+\.)Create\(OpCodes.Nop\); - \s+\kAppend\(nop_\d+\); + \s+var lbl_label_\d+ = (?il_topLevelMain_\d+\.)Create\(OpCodes.Nop\); + \s+\kAppend\(lbl_label_\d+\); (?\s+\kEmit\(OpCodes\.)Ldloca, l_enumerator_\d+\); \kConstrained, st_enumerator_0\); \kCallvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.IDisposable\), "Dispose",.+\)\)\); @@ -200,8 +200,8 @@ int Use(int t) var cecilifiedCode = result.GeneratedCode.ReadToEnd(); Assert.That(cecilifiedCode, Does.Match(""" //finally start - \s+var nop_\d+ = il_use_\d+.Create\(OpCodes.Nop\); - \s+(il_use_\d+)\.Append\(nop_\d+\); + \s+var lbl_label_\d+ = il_use_\d+.Create\(OpCodes.Nop\); + \s+(il_use_\d+)\.Append\(lbl_label_\d+\); \s+var (?nop_\d+) = \1.Create\(OpCodes.Nop\); (?\s+\1\.Emit\(OpCodes\.)Ldloc, (?l_enumerator_\d+)\); \3Brfalse_S, \k\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/Framework/CecilifierContextBasedTestBase.cs b/Cecilifier.Core.Tests/Tests/Unit/Framework/CecilifierContextBasedTestBase.cs index 053ecf95..2808eacf 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/Framework/CecilifierContextBasedTestBase.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/Framework/CecilifierContextBasedTestBase.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Cecilifier.ApiDriver.MonoCecil; using Cecilifier.Core.AST; -using Cecilifier.Core.Misc; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/Cecilifier.Core.Tests/Tests/Unit/GenericTests.cs b/Cecilifier.Core.Tests/Tests/Unit/GenericTests.cs index efdc1539..e6596f7b 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/GenericTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/GenericTests.cs @@ -106,10 +106,11 @@ public void Issue_169_ReferenceToForwardedMethodContainingGenericParameters() @"m_M_2.ReturnType = gp_T_1;\s+" + @"cls_foo_0.Methods.Add\(m_M_2\);\s+" + @"m_M_2.Body.InitLocals = true;\s+" + - @"var il_M_3 = m_M_2.Body.GetILProcessor\(\);\s+" + @"//Parameters of 'T M\(T t\) { T tl = t; t = tl; return t; }'\s+" + @"var p_t_4 = new ParameterDefinition\(""t"", ParameterAttributes.None, gp_T_1\);\s+" + - @"m_M_2.Parameters.Add\(p_t_4\);\s+")); + @"m_M_2.Parameters.Add\(p_t_4\);\s+" + + @"var il_M_3 = m_M_2.Body.GetILProcessor\(\);\s+" + )); Assert.That(cecilifiedCode, Does.Match(""" //t = tl; @@ -140,6 +141,7 @@ public void TestInstantiatingGenericTypeParameter(string code) \s+r_createInstance_4.ReturnType = gi_T_5; \s+var gi_createInstance_6 = new GenericInstanceMethod\(r_createInstance_4\); \s+gi_createInstance_6.GenericArguments.Add\(gp_T_\d+\); + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+il_M_\d+.Emit\(OpCodes.Call, gi_createInstance_6\); """)); @@ -561,6 +563,7 @@ public void MemberAccess_OnArraysOfGenericType_EmitsReadonlyPrefixBeforeLdelema( var cecilifiedCode = result.GeneratedCode.ReadToEnd(); Assert.That(cecilifiedCode, Does.Match(""" \s+//array\[0\].ToString\(\) + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); (?\s+il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\); \kLdc_I4, 0\); \kReadonly\); @@ -672,8 +675,8 @@ static TestCaseData[] Scenarios() \s+\k.GenericParameters.Add\(gp_T_4\); \s+cls_inner_\d+.Methods.Add\(\k\); \s+\k.Body.InitLocals = true; - \s+var il_M_3 = \k.Body.GetILProcessor\(\); \s+//M\(\) + \s+var il_M_3 = \k.Body.GetILProcessor\(\); \s+il_M_3.Emit\(OpCodes.Ldarg_0\); \s+var (?gi_M_\d+) = new GenericInstanceMethod\(\k\); \s+\k.GenericArguments.Add\(gp_T_4\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/InterfaceTests.cs b/Cecilifier.Core.Tests/Tests/Unit/InterfaceTests.cs index b2d04e22..1c54c120 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/InterfaceTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/InterfaceTests.cs @@ -61,7 +61,6 @@ public void AbstractStaticMethodReferenceTest(string code, string expectedReturn var (m_M_\d+) = new MethodDefinition\("IFoo.M",.+\); \s+(cls_foo_\d+).Methods.Add\(\1\); \s+m_M_5.Body.InitLocals = true; - \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+m_M_\d+.Overrides.Add\(new MethodReference\(m_M_2.Name, m_M_2.ReturnType\) {.+DeclaringType = itf_iFoo_\d+.MakeGenericInstanceType\(\2\),.+\); """, TestName = "Generics")] diff --git a/Cecilifier.Core.Tests/Tests/Unit/MappingTests.cs b/Cecilifier.Core.Tests/Tests/Unit/MappingTests.cs index 8e9957e9..237f3562 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MappingTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MappingTests.cs @@ -31,8 +31,8 @@ public void Test_CecilifierPreamble_LineCount() [Test] public void Test_ClassAndMethod_InSingleLine() { - // 1 2 3 4 5 - // 12345678901234567890123456789012345678901234567890 + // 1 2 3 4 5 + // 12345678901234567890123456789012345678901234567890 var result = RunCecilifier("class Foo { int Sum(int i, int j) => i + j; }"); var message = $"Actual Mapping:{Environment.NewLine}{result.Mappings.DumpAsString()}\n\n{result.GeneratedCode.ReadToEnd()}"; @@ -59,22 +59,22 @@ public void Test_ClassAndMethod_InSingleLine() Assert.That(result.Mappings[2].Source.Begin.Column, Is.EqualTo(21), message); Assert.That(result.Mappings[2].Source.End.Column, Is.EqualTo(26), message); - Assert.That(result.Mappings[2].Cecilified.Begin.Line, Is.EqualTo(38), message); - Assert.That(result.Mappings[2].Cecilified.End.Line, Is.EqualTo(40), message); + Assert.That(result.Mappings[2].Cecilified.Begin.Line, Is.EqualTo(37), message); + Assert.That(result.Mappings[2].Cecilified.End.Line, Is.EqualTo(39), message); // parameter j Assert.That(result.Mappings[3].Source.Begin.Line, Is.EqualTo(1), message); Assert.That(result.Mappings[3].Source.Begin.Column, Is.EqualTo(28), message); Assert.That(result.Mappings[3].Source.End.Column, Is.EqualTo(33), message); - Assert.That(result.Mappings[3].Cecilified.Begin.Line, Is.EqualTo(40), message); - Assert.That(result.Mappings[3].Cecilified.End.Line, Is.EqualTo(42), message); + Assert.That(result.Mappings[3].Cecilified.Begin.Line, Is.EqualTo(39), message); + Assert.That(result.Mappings[3].Cecilified.End.Line, Is.EqualTo(41), message); // => i + j; Assert.That(result.Mappings[4].Source.Begin.Line, Is.EqualTo(1), message); Assert.That(result.Mappings[4].Source.Begin.Column, Is.EqualTo(35), message); - Assert.That(result.Mappings[4].Cecilified.Begin.Line, Is.EqualTo(42), message); + Assert.That(result.Mappings[4].Cecilified.Begin.Line, Is.EqualTo(41), message); Assert.That(result.Mappings[4].Cecilified.End.Line, Is.EqualTo(46), message); } diff --git a/Cecilifier.Core.Tests/Tests/Unit/MemberAccessTests.cs b/Cecilifier.Core.Tests/Tests/Unit/MemberAccessTests.cs index da189e02..9d92e46e 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MemberAccessTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MemberAccessTests.cs @@ -25,6 +25,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t\.ToString\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarga, \1\); \3Constrained, \2\); \3Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Object\), "ToString",.+\)\)\); @@ -36,6 +37,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t\.Get\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarga, \1\); \3Constrained, \2\); \3Callvirt, m_get_1\); @@ -47,6 +49,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t\.ToString\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarga, \1\); \3Constrained, \2\); \3Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Object\), "ToString",.+\)\)\); @@ -58,6 +61,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t\.ToString\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarga, \1\); \3Constrained, \2\); \3Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Object\), "ToString",.+\)\)\); @@ -69,6 +73,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t\.ToString\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarg_1\); \3Box, \2\); \3Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Object\), "ToString",.+\)\)\); @@ -80,6 +85,7 @@ public void TestRefTarget_Class(string target) \s+var (p_t_\d+) = new ParameterDefinition\("t", ParameterAttributes.None, (gp_T_\d+)\); \s+m_C_\d+.Parameters.Add\(\1\); \s+//t.ToString\(\) + \s+var il_C_\d+ = m_C_\d+.Body.GetILProcessor\(\); (\s+il_C_\d+\.Emit\(OpCodes\.)Ldarg_1\); \3Box, \2\); \3Callvirt, .+ImportReference\(.+ResolveMethod\(typeof\(System.Object\), "ToString",.+\)\)\); @@ -268,24 +274,25 @@ public override event System.EventHandler MyEvent remove { base.MyEvent -= value; } } } - """, true, TestName = "Same compilation Unit (Event)")] + """, false, true, TestName = "Same compilation Unit (Event)")] [TestCase(""" using System; class Derived : Exception { public override Exception GetBaseException() => base.GetBaseException(); } - """, TestName = "External type")] - public void AccessingMember_ThroughBaseKeyword(string source, bool requiresExtraParameterInCall = false) + """, true, TestName = "External type")] + public void AccessingMember_ThroughBaseKeyword(string source, bool checkIlVar = false, bool requiresExtraParameterInCall = false) { var result = RunCecilifier(source); var cecilifiedCode = result.GeneratedCode.ReadToEnd(); var pattern = $""" \s+//.*base\..+(?:\(\))?;? + { (checkIlVar ? @"\s+var il_getBaseException_\d+ = m_getBaseException_\d+.Body.GetILProcessor\(\);" : "")} (\s+il_.+\d+\.Emit\(OpCodes\.)Ldarg_0\); { (requiresExtraParameterInCall ? @"(\s+il_.+\d+\.Emit\(OpCodes\.)Ldarg_1\);" : "")} \1Call,.+\); """; - if (!requiresExtraParameterInCall) + if (!requiresExtraParameterInCall || !checkIlVar) pattern = pattern.Replace("\n\n", "\n"); Assert.That(cecilifiedCode, Does.Match(pattern)); @@ -311,6 +318,7 @@ class C var actual = result.GeneratedCode.ReadToEnd(); Assert.That(actual, Does.Match(""" //field = toSet + \s+var il_set_\d+ = m_set_\d+.Body.GetILProcessor\(\); (?\s+il_set_\d+\.Emit\(OpCodes\.)Ldarg_0\); \kLdarg_1\); \kStfld, new FieldReference\(fld_field_\d+.Name, fld_field_\d+.FieldType, cls_C_\d+.MakeGenericInstanceType\(gp_T_\d+\)\)\); @@ -318,6 +326,7 @@ class C Assert.That(actual, Does.Match(""" //Property = toSet + \s+var il_setProperty_\d+ = m_setProperty_\d+.Body.GetILProcessor\(\); (?\s+il_setProperty_\d+\.Emit\(OpCodes\.)Ldarg_0\); \kLdarg_1\); \s+var (?r_set_Property_\d+) = new MethodReference\(l_set_\d+.Name, l_set_\d+.ReturnType\) {.+DeclaringType = cls_C_\d+.MakeGenericInstanceType\(gp_T_\d+\).+}; @@ -353,6 +362,7 @@ ref struct Ref var actual = result.GeneratedCode.ReadToEnd(); Assert.That(actual, Does.Match(""" //Console.WriteLine\(item\) + \s+var il_print_\d+ = m_print_\d+.Body.GetILProcessor\(\); (?\s+il_print_\d+.Emit\(OpCodes\.)Ldarg_0\); \kLdfld, new FieldReference\((?fld_item_\d+).Name, \k.FieldType, st_ref_0.MakeGenericInstanceType\(gp_T_1\).+\); \kLdobj, gp_T_1\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/MemberDependencies/MemberDependencyTestBase.cs b/Cecilifier.Core.Tests/Tests/Unit/MemberDependencies/MemberDependencyTestBase.cs index 21ca1be0..3bab93b0 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MemberDependencies/MemberDependencyTestBase.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MemberDependencies/MemberDependencyTestBase.cs @@ -23,7 +23,7 @@ private protected IReadOnlyCollection CollectDependenciesFromS protected static CSharpCompilation CompilationFor(params string[] code) { var syntaxTrees = code.Select(source => CSharpSyntaxTree.ParseText(source)); - var comp = CSharpCompilation.Create("Test", syntaxTrees, Basic.Reference.Assemblies.Net90.References.All, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + var comp = CSharpCompilation.Create("Test", syntaxTrees, Basic.Reference.Assemblies.Net100.References.All, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var errors = comp.GetDiagnostics() .Where(d => d.Severity == DiagnosticSeverity.Error) diff --git a/Cecilifier.Core.Tests/Tests/Unit/MethodDefinitionVariableTests.cs b/Cecilifier.Core.Tests/Tests/Unit/MethodDefinitionVariableTests.cs index 6a5f6e2d..b196600b 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MethodDefinitionVariableTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MethodDefinitionVariableTests.cs @@ -11,18 +11,18 @@ public class MethodDefinitionVariableTests public void EqualsTests([Values] VariableMemberKind kind, [Values("Parent", null)] string parentTypeName, [Values("p1", null)] string parameter) { var parameters = parameter != null ? new[] { parameter } : Array.Empty(); - var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, 0); + var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, []); Assert.That(tbt.Equals(tbt), Is.True); } [Test] - public void EqualityOperatorsTests([Values] VariableMemberKind kind, [Values("Parent", null)] string parentTypeName, [Values(0, 1, 2)] byte typeParameterCount, [Values("p1", null)] string parameter) + public void EqualityOperatorsTests([Values] VariableMemberKind kind, [Values("Parent", null)] string parentTypeName, [Values(new string[0], new[] { "A" }, new[] { "A", "B"})] string[] typeParameters, [Values("p1", null)] string parameter) { var parameters = parameter != null ? [parameter] : Array.Empty(); - var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, typeParameterCount); - var shouldBeEqual = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, typeParameterCount); + var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, typeParameters); + var shouldBeEqual = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, typeParameters); - const byte TypeParameterCountNotMatchingAnyArgumentForTest = 42; + string[] TypeParameterCountNotMatchingAnyArgumentForTest = ["A", "B", "C"]; var shouldNotBeEqual = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, TypeParameterCountNotMatchingAnyArgumentForTest); Assert.That(tbt.Equals(shouldBeEqual), Is.True); @@ -38,8 +38,8 @@ public void EqualityOperatorsTests([Values] VariableMemberKind kind, [Values("Pa public void GetHashCodeTests([Values] VariableMemberKind kind, [Values("Parent", null)] string parentTypeName, [Values("p1", null)] string parameter) { var parameters = parameter != null ? [parameter] : Array.Empty(); - var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, 0); - var shouldBeEqual = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, 0); + var tbt = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, []); + var shouldBeEqual = new MethodDefinitionVariable(parentTypeName, "methodName", parameters, []); Assert.That(tbt.GetHashCode(), Is.EqualTo(shouldBeEqual.GetHashCode())); } diff --git a/Cecilifier.Core.Tests/Tests/Unit/MethodExtensionsTests.cs b/Cecilifier.Core.Tests/Tests/Unit/MethodExtensionsTests.cs index dbcb3f6a..75901b38 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MethodExtensionsTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MethodExtensionsTests.cs @@ -2,7 +2,10 @@ using System.Diagnostics; using System.Linq; using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; +using Cecilifier.Core.AST; using Cecilifier.Core.Extensions; +using Cecilifier.Core.Tests.Framework.Attributes; using Cecilifier.Core.Tests.Tests.Unit.Framework; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -13,8 +16,11 @@ namespace Cecilifier.Core.Tests.Tests.Unit; #nullable enable -[TestFixture] -internal class MethodExtensionsTests : CecilifierContextBasedTestBase +[TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] +[EnableForContext(IgnoreReason = "Not Supported")] + +internal class MethodExtensionsTests : CecilifierContextBasedTestBase where TContext: IVisitorContext { protected override string Snippet => """ using System.Threading.Tasks; diff --git a/Cecilifier.Core.Tests/Tests/Unit/MethodTests.cs b/Cecilifier.Core.Tests/Tests/Unit/MethodTests.cs index 82e6e511..1807caed 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/MethodTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/MethodTests.cs @@ -37,7 +37,7 @@ public void CovariantReturnMethod() Assert.That(cecilifiedCode, Does.Match("""var m_get_6 = new MethodDefinition\("Get",.+cls_D_\d+\);""")); Assert.That(cecilifiedCode, Does.Match(@"m_get_6\.CustomAttributes\.Add\(.+typeof\(.+PreserveBaseOverridesAttribute\).+\);")); Assert.That(cecilifiedCode, Contains.Substring("m_get_6.Overrides.Add(m_get_1);")); - Assert.That(cecilifiedCode, Contains.Substring("il_callIt_10.Emit(OpCodes.Callvirt, m_get_6);")); + Assert.That(cecilifiedCode, Contains.Substring("il_callIt_10.Emit(OpCodes.Callvirt, m_get_1);")); } [Test] @@ -71,8 +71,8 @@ class Derived : Base \s+var (m_callOverloadedInType_\d+) = new MethodDefinition\("CallOverloadedInType", MethodAttributes.Public \| MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.String\); \s+cls_derived_5.Methods.Add\(\1\); \s+\1\.Body\.InitLocals = true; - \s+var (il_callOverloadedInType_\d+) = \1\.Body\.GetILProcessor\(\); \s+//FromBase\(\) + \s+var (il_callOverloadedInType_\d+) = \1\.Body\.GetILProcessor\(\); \s+(\2\.Emit\(OpCodes\.)Ldarg_0\); \s+\3Callvirt, m_fromBase_\d+\); \s+\3Ret\); @@ -86,8 +86,8 @@ class Derived : Base \s+var m_callBase_\d+ = new MethodDefinition\("CallBase", MethodAttributes.Public \| MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.String\); \s+cls_derived_5.Methods.Add\(m_callBase_\d+\); \s+m_callBase_\d+.Body.InitLocals = true; - \s+var (il_callBase_\d+) = m_callBase_8.Body.GetILProcessor\(\); \s+//GetType\(\).FullName + \s+var (il_callBase_\d+) = m_callBase_8.Body.GetILProcessor\(\); \s+(\1\.Emit\(OpCodes\.)Ldarg_0\); \s+\2Call, .+"GetType".+\); \s+\2Callvirt,.+"get_FullName".+\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/NullCoalescingTests.cs b/Cecilifier.Core.Tests/Tests/Unit/NullCoalescingTests.cs index 33cf0f6e..9a998176 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/NullCoalescingTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/NullCoalescingTests.cs @@ -15,13 +15,14 @@ public void Simplest_ReferenceTypes() result.GeneratedCode.ReadToEnd(), Does.Match(""" //o1 \?\? o2 + \s+var il_M_\d+ = m_M_6.Body.GetILProcessor\(\); \s+var return_\d+ = (?il_M_\d+)\.Create\(OpCodes.Nop\); (?\s+\k\.Emit\(OpCodes\.)Ldarg_0\); \kDup\); \kBrtrue_S, return_\d+\); \kPop\); \kLdarg_1\); - \s+\k\.Body\.Instructions\.Add\(return_\d+\); + \s+\k\.Append\(return_\d+\); \kRet\); """)); } @@ -34,6 +35,7 @@ public void LeftExpression_IsEvaluated_OnlyOnce() result.GeneratedCode.ReadToEnd(), Does.Match(""" //M2\(n\) \?\? o2 + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+var return_\d+ = (?il_M_\d+\.)Create\(OpCodes.Nop\); (?\s+\kEmit\(OpCodes\.)Ldarg_0\); \kCall, m_m2_\d+\); @@ -41,7 +43,7 @@ public void LeftExpression_IsEvaluated_OnlyOnce() \kBrtrue_S, return_\d+\); \kPop\); \kLdarg_1\); - \s+\kBody\.Instructions.Add\(return_\d+\); + \s+\kAppend\(return_\d+\); \kRet\); \s+//End of local function """)); @@ -53,18 +55,19 @@ public void SimpleNullableValueType() var result = RunCecilifier("int? M(int? i1, int? i2) => i1 ?? i2;"); Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(""" //i1 \?\? i2 + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); (?\s+il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+var (?l_leftValue_\d+) = new VariableDefinition\(.+ImportReference\(.+Nullable<>\)\)\.MakeGenericInstanceType\(.+Int32\)\); \s+m_M_\d+.Body.Variables.Add\(\k\); \kStloc, \k\); \kLdloca_S, \k\); \kCall,.+typeof\(System.Nullable\).+"get_HasValue".+\); - \s+var (?loadLeftValueTarget_\d+) = il_M_\d+.Create\(OpCodes.Ldloc_S, \k\); + \s+var (?loadLeftValueTarget_\d+) = il_M_\d+.Create\(OpCodes.Nop\); \kBrtrue_S, \k\); \kLdarg_1\); \kRet\); - \s+il_M_\d+\.Body\.Instructions.Add\(\k\); - \kRet\); + \s+il_M_\d+\.Append\(\k\); + \kLdloc, \k\); """)); } @@ -90,11 +93,20 @@ public void MixedNullableValueType_AndReferenceType() (?\s+il_m3_\d+\.Emit\(OpCodes\.)Brtrue_S, (?loadLeftValueTarget_\d+)\); \kLdarg_1\); \kUnbox_Any, assembly.MainModule.TypeSystem.Int32\); - \kNewobj,.+System.Nullable<>.+MakeGenericType\(typeof\(System.Int32\)\).GetConstructors\(\).Single\(ctor => ctor.GetParameters\(\).Length == 1\)\)\); - \kRet\); - \s+il_m3_\d+\.Body\.Instructions\.Add\(\k\); + \s+var (?m_declaringType_\d+) = assembly.MainModule.ImportReference\(typeof\(System.Nullable<>\)\).MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\); + \s+var (?r_tmpMethod_\d+) = assembly.MainModule.ImportReference\(\k.ElementType.Resolve\(\).Methods.Single\(m => m.Name == ".ctor" && m.Parameters.Count == 1\)\); + \s+var r_genericMethod_\d+ = new MethodReference\(\k.Name, \k.ReturnType\) + \s+{ + \s+HasThis = \k.HasThis, + \s+CallingConvention = \k.CallingConvention, + \s+ExplicitThis = \k.ExplicitThis, + \s+DeclaringType = \k.DeclaringType.MakeGenericInstanceType\(\k.GenericArguments.ToArray\(\)\), + \s+}; + \s+r_genericMethod_\d+.Parameters.Add\(new ParameterDefinition\(\k.Parameters\[0\].Name, \k.Parameters\[0\].Attributes, \k.Parameters\[0\].ParameterType\)\); + \kNewobj, r_genericMethod_18\); \kRet\); - \s+//End of local function\. + \s+il_m3_\d+\.Append\(\k\); + \kLdloc, l_leftValue_\d+\); """)); } } diff --git a/Cecilifier.Core.Tests/Tests/Unit/NullableTests.cs b/Cecilifier.Core.Tests/Tests/Unit/NullableTests.cs index 6d436c98..98f7ab17 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/NullableTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/NullableTests.cs @@ -110,6 +110,7 @@ public void ConstructorIsInvokedAfterCast() var result = RunCecilifier("""int? M(object o) => (int) o;"""); Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(""" \s+//\(int\) o + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); (?\s+il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\); \kUnbox_Any, assembly.MainModule.TypeSystem.Int32\); \kNewobj,.+ImportReference\(typeof\(System.Nullable<>\)\.MakeGenericType\(typeof\(System.Int32\)\)\.GetConstructors\(\).+\); @@ -122,6 +123,7 @@ public void ConstructorIsInvokedAfterCast() "class C { void M(int? p) => p = null; }", """ //p = null + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); \s+il_M_2.Emit\(OpCodes.Ldarga, p_p_\d+\); \s+il_M_2.Emit\(OpCodes.Initobj, .+ImportReference\(typeof\(System.Nullable<>\)\).MakeGenericInstanceType\(.+Int32\)\); """, @@ -148,6 +150,7 @@ public void ConstructorIsInvokedAfterCast() "class C { void M(int? p) => M(null); }", """ //M\(null\) + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); (\s+il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+var (?l_tmpNull_\d+) = new VariableDefinition\(.+ImportReference\(.+System.Nullable<>\)\).MakeGenericInstanceType\(.+Int32\)\); \s+m_M_\d+.Body.Variables.Add\(\k\); @@ -161,6 +164,7 @@ public void ConstructorIsInvokedAfterCast() "class C { void M(int? p) => f = null; int ?f; }", """ //f = null + \s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\); (\s+il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\); \1Ldflda, fld_f_\d+\); \1Initobj, .+ImportReference\(typeof\(System.Nullable<>\)\).MakeGenericInstanceType\(.+Int32\)\); diff --git a/Cecilifier.Core.Tests/Tests/Unit/OperatorsTests.cs b/Cecilifier.Core.Tests/Tests/Unit/OperatorsTests.cs index 81268cc5..8153ce14 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/OperatorsTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/OperatorsTests.cs @@ -241,6 +241,7 @@ public void TestNullConditionalOperatorOnComplexTargets(string target, string ex var (p_o_\d+) = new ParameterDefinition\("o", ParameterAttributes.None, assembly.MainModule.TypeSystem.Object\); \s+m_foo_\d+.Parameters.Add\(\1\); \s+//o != null + \s+var il_foo_\d+ = m_foo_\d+.Body.GetILProcessor\(\); (\s+il_foo_\d+\.Emit\(OpCodes\.)Ldarg_0\); \2Ldnull\); \2Ceq\); @@ -255,6 +256,7 @@ public void TestNullConditionalOperatorOnComplexTargets(string target, string ex var (p_o_\d+) = new ParameterDefinition\("o", ParameterAttributes.None, assembly.MainModule.TypeSystem.Object\); \s+m_foo_\d+.Parameters.Add\(\1\); \s+//o == null + \s+var il_foo_\d+ = m_foo_\d+.Body.GetILProcessor\(\); (\s+il_foo_\d+\.Emit\(OpCodes\.)Ldarg_0\); \2Ldnull\); \2Ceq\); @@ -266,11 +268,12 @@ public void TestNullConditionalOperatorOnComplexTargets(string target, string ex var (p_o_\d+) = new ParameterDefinition\("o", ParameterAttributes.None, (?gp_T_\d+)\); \s+m_foo_\d+.Parameters.Add\(\1\); \s+//o == null - (\s+il_foo_\d+\.Emit\(OpCodes\.)Ldarg_0\); - \2Box, \k\); - \2Ldnull\); - \2Ceq\); - \2Ret\); + \s+var il_foo_\d+ = m_foo_\d+.Body.GetILProcessor\(\); + (?\s+il_foo_\d+\.Emit\(OpCodes\.)Ldarg_0\); + \kBox, \k\); + \kLdnull\); + \kCeq\); + \kRet\); """, TestName = "Type Parameter ==")] public void EqualityAndInequalityOperators_AgainstNull_DoesNotInvokeOverloadOnSystemObject(string code, string expected) { diff --git a/Cecilifier.Core.Tests/Tests/Unit/ParamsTests.cs b/Cecilifier.Core.Tests/Tests/Unit/ParamsTests.cs index 6d556022..afe491e7 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/ParamsTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/ParamsTests.cs @@ -127,6 +127,7 @@ static IEnumerable CallSiteTestScenarios() new [] { """ //M\(1, 2, n\) + \s+var il_use_\d+ = m_use_\d+.Body.GetILProcessor\(\); (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+var (?l_itemsParams_\d+) = new VariableDefinition\(.+Int32.MakeArrayType\(\)\); \s+m_use_\d+.Body.Variables.Add\(\k\); @@ -139,6 +140,7 @@ static IEnumerable CallSiteTestScenarios() new [] { """ //M\(1, 2, n\) + \s+var il_use_\d+ = m_use_\d+.Body.GetILProcessor\(\); (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+//InlineArray to store the `params` values. """ @@ -150,6 +152,7 @@ static IEnumerable CallSiteTestScenarios() { """ //M\(1, 2, n\) + \s+var il_use_\d+ = m_use_\d+.Body.GetILProcessor\(\); (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+//InlineArray to store the `params` values. """, @@ -186,6 +189,7 @@ static IEnumerable CallSiteTestScenarios() { """ //M\(1, 2, n\) + \s+var il_use_\d+ = m_use_\d+.Body.GetILProcessor\(\); (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldarg_0\); \s+//Instantiates a List passing the # of elements to its ctor. \kLdc_I4, 3\); @@ -217,41 +221,5 @@ static IEnumerable CallSiteTestScenarios() "ICollection", (string[]) ilistTestData.Arguments[1]!) // call site code generated for ICollection is identical to the code generated for IList. .SetName("ICollection"); - - yield return new TestCaseData( - "ReadOnlySpan", - new [] - { - """ - //M\(1, 2, n\) - (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldarg_0\); - \s+//InlineArray to store the `params` values. - """, - - """ - var (?l_itemsArg_\d+) = new VariableDefinition\(st_inlineArray_\d+.MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\)\); - \s+m_use_\d+.Body.Variables.Add\(\k\); - (?\s+il_use_\d+\.Emit\(OpCodes\.)Ldloca_S, \k\); - \kInitobj, st_inlineArray_\d+.MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\)\); - \kLdloca_S, \k\); - \kLdc_I4, 0\); - """, // Declares and initialize a variable of an inline array type used to store the data. - - """ - (?\s+il_use_\d+\.Emit\(OpCodes\.)Call, gi_inlineArrayElementRef_\d+\); - \kLdc_I4, 1\); - \kStind_I4\); - \kLdloca_S, (?l_itemsArg_\d+)\); - \kLdc_I4, 1\); - \kCall, gi_inlineArrayElementRef_\d+\); - \kLdc_I4, 2\); - \kStind_I4\); - \kLdloca_S, \k\); - \kLdc_I4, 2\); - \kCall, gi_inlineArrayElementRef_\d+\); - \kLdarg_1\); - \kStind_I4\); - """ // Populates inline array with data to be passed to params[] - }).SetName("ReadOnlySpan"); } } diff --git a/Cecilifier.Core.Tests/Tests/Unit/PropertyTests.cs b/Cecilifier.Core.Tests/Tests/Unit/PropertyTests.cs index 84601b6d..f6aba574 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/PropertyTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/PropertyTests.cs @@ -98,7 +98,7 @@ public void Covariant() Assert.That(cecilifiedCode, Contains.Substring("var m_get_8 = new MethodDefinition(\"get_Prop\", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot, cls_D_6);")); Assert.That(cecilifiedCode, Does.Match(@"m_get_8\.CustomAttributes\.Add\(.+typeof\(.+PreserveBaseOverridesAttribute\).+\);")); - Assert.That(cecilifiedCode, Contains.Substring("il_callIt_12.Emit(OpCodes.Callvirt, m_get_8);")); + Assert.That(cecilifiedCode, Contains.Substring("il_callIt_12.Emit(OpCodes.Callvirt, m_get_2);")); } [Test] diff --git a/Cecilifier.Core.Tests/Tests/Unit/StackallocTests.cs b/Cecilifier.Core.Tests/Tests/Unit/StackallocTests.cs index 6482b4bc..d6461bb9 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/StackallocTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/StackallocTests.cs @@ -100,7 +100,7 @@ public void Test_Issue_133_Span_InitializedByStackallocWithSizeFromParameter_Pas { "il_bar_2.Emit(OpCodes.Ldc_I4, 4000);", "il_bar_2.Emit(OpCodes.Conv_U);", "il_bar_2.Emit(OpCodes.Localloc);", "il_bar_2.Emit(OpCodes.Ldc_I4, 4000);", "var l_spanCtor_4 = new MethodReference(\".ctor\", assembly.MainModule.TypeSystem.Void, assembly.MainModule.ImportReference(typeof(System.Span<>)).MakeGenericInstanceType(assembly.MainModule.TypeSystem.Int32)) { HasThis = true };", - "l_spanCtor_4.Parameters.Add(new ParameterDefinition(\"ptr\", ParameterAttributes.None, assembly.MainModule.ImportReference(typeof(void*))));", + "l_spanCtor_4.Parameters.Add(new ParameterDefinition(\"ptr\", ParameterAttributes.None, assembly.MainModule.TypeSystem.Void.MakePointerType()));", "l_spanCtor_4.Parameters.Add(new ParameterDefinition(\"length\", ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32));", "il_bar_2.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(l_spanCtor_4));", "il_bar_2.Emit(OpCodes.Call, m_bar_1);", "il_bar_2.Emit(OpCodes.Ret);", }; @@ -122,7 +122,7 @@ public void Test_Issue_133_Span_InitializedByStackallocWithSizeFromField_PassedA "il_bar_3.Emit(OpCodes.Ldsfld, fld_countField_1);", "il_bar_3.Emit(OpCodes.Conv_U);", "il_bar_3.Emit(OpCodes.Sizeof, assembly.MainModule.TypeSystem.Int32);", "il_bar_3.Emit(OpCodes.Mul_Ovf_Un);", "il_bar_3.Emit(OpCodes.Localloc);", "il_bar_3.Emit(OpCodes.Ldfld, fld_countField_1);", "var l_spanCtor_5 = new MethodReference(\".ctor\", assembly.MainModule.TypeSystem.Void, assembly.MainModule.ImportReference(typeof(System.Span<>)).MakeGenericInstanceType(assembly.MainModule.TypeSystem.Int32)) { HasThis = true };", - "l_spanCtor_5.Parameters.Add(new ParameterDefinition(\"ptr\", ParameterAttributes.None, assembly.MainModule.ImportReference(typeof(void*))));", + "l_spanCtor_5.Parameters.Add(new ParameterDefinition(\"ptr\", ParameterAttributes.None, assembly.MainModule.TypeSystem.Void.MakePointerType()));", "l_spanCtor_5.Parameters.Add(new ParameterDefinition(\"length\", ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32));", "il_bar_3.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(l_spanCtor_5));", "il_bar_3.Emit(OpCodes.Call, m_bar_2);", "il_bar_3.Emit(OpCodes.Ret);", }; @@ -175,7 +175,7 @@ public void ImplicitStackAllocInNestedExpressions() Assert.That(cecilifiedCode, Does.Match( @"var (l_spanCtor_\d+) = new MethodReference\("".ctor"", assembly.MainModule.TypeSystem.Void, assembly.MainModule.ImportReference\(typeof\(System\.Span<>\)\).MakeGenericInstanceType\(.+TypeSystem.Int32\)\) { HasThis = true };\s+" + - @"\1.Parameters.Add\(.+""ptr"".+void\*.+\);\s+" + + @"\1.Parameters.Add\(.+""ptr"".+assembly.MainModule.TypeSystem.Void.MakePointerType\(\).+\);\s+" + @"\1.Parameters.Add\(.+""length"".+TypeSystem.Int32.+\);\s+" + @"il_M_\d+.Emit\(OpCodes.Newobj, assembly.MainModule.ImportReference\(\1\)\);")); } @@ -189,7 +189,7 @@ public void ExplicitStackAllocInNestedExpressions() Assert.That(cecilifiedCode, Does.Match( @"var (l_spanCtor_\d+) = new MethodReference\("".ctor"", assembly.MainModule.TypeSystem.Void, assembly.MainModule.ImportReference\(typeof\(System\.Span<>\)\).MakeGenericInstanceType\(.+TypeSystem.Int32\)\) { HasThis = true };\s+" + - @"\1.Parameters.Add\(.+""ptr"".+void\*.+\);\s+" + + @"\1.Parameters.Add\(.+""ptr"".+assembly.MainModule.TypeSystem.Void.MakePointerType().+\);\s+" + @"\1.Parameters.Add\(.+""length"".+TypeSystem.Int32.+\);\s+" + @"il_M_\d+.Emit\(OpCodes.Newobj, assembly.MainModule.ImportReference\(\1\)\);")); } diff --git a/Cecilifier.Core.Tests/Tests/Unit/StructSpecificTests.cs b/Cecilifier.Core.Tests/Tests/Unit/StructSpecificTests.cs index f4d3b974..60722df9 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/StructSpecificTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/StructSpecificTests.cs @@ -162,9 +162,9 @@ void M(System.IDisposable parameter) """)); } - [TestCase("=> new Test();", TestName = "Bodied")] - [TestCase("{ return new Test(); }", TestName = "Return")] - public void ReturnStructInstantiationAsReferenceType(string body) + [TestCase("=> new Test();", true, TestName = "Bodied")] + [TestCase("{ return new Test(); }", false, TestName = "Return")] + public void ReturnStructInstantiationAsReferenceType(string body, bool checkIlVariable) { var result = RunCecilifier( $$""" @@ -177,10 +177,10 @@ System.IDisposable M() {{body}} var cecilifiedCode = result.GeneratedCode.ReadToEnd(); Assert.That(cecilifiedCode, Does.Match( - """ + $""" \s+//(?:return )?new Test\(\);? .+var (l_vt_\d+) = new VariableDefinition\((st_test_\d+)\); - .+m_M_3.Body.Variables.Add\(\1\); + .+m_M_3.Body.Variables.Add\(\1\);{(checkIlVariable ? @"\n\s+var il_M_\d+ = m_M_\d+.Body.GetILProcessor\(\);": "")} (.+il_M_\d+.Emit\(OpCodes\.)Ldloca_S, \1\); \3Initobj, \2\); \3Ldloc, \1\); @@ -231,6 +231,7 @@ class Foo \s+var p_i_4 = new ParameterDefinition\("i", ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32\); \s+m_ternaryOperators_2.Parameters.Add\(p_i_4\); \s+//i == 2 \? new S\(\): new S\(\) + \s+var il_ternaryOperators_\d+ = m_ternaryOperators_\d+.Body.GetILProcessor\(\); \s+var lbl_conditionEnd_5 = il_ternaryOperators_3.Create\(OpCodes.Nop\); \s+var lbl_whenFalse_6 = il_ternaryOperators_3.Create\(OpCodes.Nop\); (\s+il_ternaryOperators_\d+\.Emit\(OpCodes\.)Ldarg_1\); @@ -474,6 +475,7 @@ class Field where T : struct, Itf """, """ //field.M\(42\) + \s+var il_call_\d+ = m_call_\d+.Body.GetILProcessor\(\); \s+il_call_\d+.Emit\(OpCodes\.Ldarg_0\); \s+il_call_\d+.Emit\(OpCodes\.Ldflda, new FieldReference\(fld_field_\d+.Name, fld_field_\d+.FieldType, cls_field_\d+.MakeGenericInstanceType\(gp_T_\d+\)\)\); """, diff --git a/Cecilifier.Core.Tests/Tests/Unit/TypeResolverTests.cs b/Cecilifier.Core.Tests/Tests/Unit/TypeResolverTests.cs index 410982ab..c7415bda 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/TypeResolverTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/TypeResolverTests.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using System.Linq; using Cecilifier.ApiDriver.MonoCecil; +using Cecilifier.ApiDriver.SystemReflectionMetadata; using Cecilifier.Core.AST; using Cecilifier.Core.Extensions; +using Cecilifier.Core.Tests.Framework.Attributes; using Cecilifier.Core.Tests.Tests.Unit.Framework; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; @@ -16,6 +18,8 @@ namespace Cecilifier.Core.Tests.Tests.Unit; #nullable enable [TestFixture(typeof(MonoCecilContext))] +[TestFixture(typeof(SystemReflectionMetadataContext))] +[EnableForContext(IgnoreReason = "Not Supported")] internal class TypeResolverTests : CecilifierContextBasedTestBase where TContext : IVisitorContext { protected override string Snippet => @@ -56,7 +60,7 @@ public void TypeParameterFromDeclaringType_Resolves_To_DeclaredVariable() // Simulates type parameter `T` being registered under type `Foo` using var _ = context.DefinitionVariables.WithCurrent("Foo", "T", VariableMemberKind.TypeParameter, "TypeParameter_T_var"); - var resolved = context.TypeResolver.ResolveAny(m1Symbol.ReturnType, m1Symbol.ToTypeResolutionContext("fakeReference")); + var resolved = context.TypeResolver.Resolve(m1Symbol.ReturnType, m1Symbol.ToTypeResolutionContext("fakeReference")); Assert.That(resolved.Expression, Does.Match(@".+ImportReference\(typeof\(System.Func<>\)\)\.MakeGenericInstanceType\(TypeParameter_T_var\)")); } @@ -70,7 +74,7 @@ public void TypeParameterFromGenericMethod_Resolves_To_DeclaredVariable() // Simulates type parameter `T` being registered under method `M2` using var _ = context.DefinitionVariables.WithCurrent("Foo.M2()", "TM", VariableMemberKind.TypeParameter, "TypeParameter_TM_var"); - var resolved = context.TypeResolver.ResolveAny(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("fakeReference")); + var resolved = context.TypeResolver.Resolve(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("fakeReference")); Assert.That(resolved.Expression, Does.Match(@".+ImportReference\(typeof\(System.Func<>\)\)\.MakeGenericInstanceType\(TypeParameter_TM_var\)")); } @@ -85,7 +89,7 @@ public void TypeParameterFromGenericMethodAndGenericType_Resolves_To_DeclaredVar // Simulates type parameters `T` & `TM` being registered under their respective members. using var t = context.DefinitionVariables.WithCurrent("Foo", "T", VariableMemberKind.TypeParameter, "TypeParameter_Foo"); using var tm = context.DefinitionVariables.WithCurrent("Foo.M3()", "TM", VariableMemberKind.TypeParameter, "TypeParameter_M3"); - var resolved = context.TypeResolver.ResolveAny(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("fakeReference")); + var resolved = context.TypeResolver.Resolve(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("fakeReference")); Assert.That(resolved.Expression, Does.Match(@".+ImportReference\(typeof\(System.Func<,>\)\)\.MakeGenericInstanceType\(TypeParameter_Foo, TypeParameter_M3\)")); } @@ -99,7 +103,7 @@ public void TypeParameterFromGenericMethodUsedAsTypeArgumentOfMethodReturn_Resol var methodSymbol = context.SemanticModel.GetSymbolInfo(convertAllInvocation.Expression).Symbol.EnsureNotNull(); // Check the return type of `ConvertAll()` invocation - var resolved = context.TypeResolver.ResolveAny(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); + var resolved = context.TypeResolver.Resolve(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); Assert.That(resolved.Expression, Does.Match(@".+ImportReference\(typeof\(System.Collections.Generic.List<>\)\)\.MakeGenericInstanceType\(methodReference.GenericParameters\[0\]\)")); } @@ -122,7 +126,7 @@ public void NestedTypes_WithTypeArguments_DefinedInCecilifiedCode(string methodN var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax).EnsureNotNull(); using var bar = context.DefinitionVariables.WithCurrent("", "Bar", VariableMemberKind.Type, "BarDefinition"); - var resolved = context.TypeResolver.ResolveAny(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); + var resolved = context.TypeResolver.Resolve(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); Assert.That( resolved.Expression, Does.Match(expectedTypeReference)); @@ -134,7 +138,7 @@ public void NonGeneric_NestedTypes() var context = NewContext(); var methodSyntax = GetMethodSyntax(context, "F"); var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodSyntax).EnsureNotNull(); - var resolved = context.TypeResolver.ResolveAny(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); + var resolved = context.TypeResolver.Resolve(methodSymbol.OriginalDefinition.ReturnType, methodSymbol.OriginalDefinition.ToTypeResolutionContext("methodReference")); Assert.That( resolved.Expression, Does.Match("""assembly.MainModule.ImportReference\(typeof\(Cecilifier.Core.Tests.Tests.Unit.D.F\)\)""")); diff --git a/Cecilifier.Core.Tests/Tests/Unit/TypeTests.Generics.cs b/Cecilifier.Core.Tests/Tests/Unit/TypeTests.Generics.cs index d2b77798..fd16bbc2 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/TypeTests.Generics.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/TypeTests.Generics.cs @@ -7,7 +7,7 @@ namespace Cecilifier.Core.Tests.Tests.Unit; public class GenericTypeTests : CecilifierUnitTestBase { [TestCase("var x = typeof(System.Collections.Generic.Dictionary.Enumerator);", - """il_topLevelMain_\d+.Emit\(OpCodes.Ldtoken, assembly.MainModule.ImportReference\(typeof\(System.Collections.Generic.Dictionary.Enumerator\)\)\);""")] + """il_topLevelMain_\d+.Emit\(OpCodes.Ldtoken, .+NewRawNestedTypeReference\("Enumerator", .+, .+ImportReference\(typeof\(.+Dictionary<,>\)\), isValueType: true, 2\).MakeGenericInstanceType\(.+Int32\)\)""")] [TestCase("class Foo { void Bar(System.Collections.Generic.Dictionary dict) { var enu = dict.GetEnumerator(); } }", """ @@ -27,7 +27,7 @@ public void TestInnerTypeOfOuterGenericType() var r = RunCecilifier("class C { System.Collections.Generic.List.Enumerator e; }"); Assert.That( r.GeneratedCode.ReadToEnd(), - Does.Match("""var fld_e_1 = new FieldDefinition\("e", FieldAttributes.Private, assembly.MainModule.ImportReference\(typeof\(System.Collections.Generic.List.Enumerator\)\)\);""")); + Does.Match("""var fld_e_1 = new FieldDefinition\("e", FieldAttributes.Private, .+NewRawNestedTypeReference\("Enumerator", module: .+, .+ImportReference\(typeof\(System.Collections.Generic.List<>\)\), isValueType: true, 1\).MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\)\);""")); } [Test] diff --git a/Cecilifier.Core/AST/ArrayInitializationProcessor.cs b/Cecilifier.Core/AST/ArrayInitializationProcessor.cs index d11825f0..43452c97 100644 --- a/Cecilifier.Core/AST/ArrayInitializationProcessor.cs +++ b/Cecilifier.Core/AST/ArrayInitializationProcessor.cs @@ -25,7 +25,7 @@ internal static void InitializeUnoptimized(ExpressionVisitor visitor, { var context = visitor.Context; var stelemOpCode = elementType.StelemOpCode(); - var resolvedElementType = context.TypeResolver.ResolveAny(elementType, ResolveTargetKind.Instruction); + var resolvedElementType = context.TypeResolver.Resolve(elementType, ResolveTargetKind.Instruction); for (var i = 0; i < elements?.Count; i++) { diff --git a/Cecilifier.Core/AST/AssignmentVisitor.cs b/Cecilifier.Core/AST/AssignmentVisitor.cs index 1cff2b4a..aa34fdf1 100644 --- a/Cecilifier.Core/AST/AssignmentVisitor.cs +++ b/Cecilifier.Core/AST/AssignmentVisitor.cs @@ -15,10 +15,10 @@ namespace Cecilifier.Core.AST { internal class AssignmentVisitor : SyntaxWalkerBase { - private readonly string ilVar; + private readonly IlContext ilVar; private readonly AssignmentExpressionSyntax assignment; - internal AssignmentVisitor(IVisitorContext ctx, string ilVar, AssignmentExpressionSyntax node) : base(ctx) + internal AssignmentVisitor(IVisitorContext ctx, IlContext ilVar, AssignmentExpressionSyntax node) : base(ctx) { this.ilVar = ilVar; assignment = node; @@ -26,7 +26,7 @@ internal AssignmentVisitor(IVisitorContext ctx, string ilVar, AssignmentExpressi PreProcessRefOutAssignments(node.Left); } - internal AssignmentVisitor(IVisitorContext ctx, string ilVar) : base(ctx) + internal AssignmentVisitor(IVisitorContext ctx, IlContext ilVar) : base(ctx) { this.ilVar = ilVar; } @@ -65,7 +65,7 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax Context.MoveLinesToEnd(InstructionPrecedingValueToLoad, lastInstructionLoadingRhs); var arrayElementType = Context.SemanticModel.GetTypeInfo(node).Type.EnsureNotNull(); var stelemOpCode = arrayElementType.StelemOpCode(); - var operand = stelemOpCode == OpCodes.Stelem ? Context.TypeResolver.ResolveAny(arrayElementType, ResolveTargetKind.Instruction) : null; + var operand = stelemOpCode == OpCodes.Stelem ? Context.TypeResolver.Resolve(arrayElementType, ResolveTargetKind.Instruction) : null; Context.ApiDriver.WriteCilInstruction(Context, ilVar, stelemOpCode, operand); } } @@ -217,7 +217,7 @@ bool HandleIndexer(SyntaxNode node, LinkedListNode lastInstructionLoadin private void EmitIndirectStore(ITypeSymbol typeBeingStored) { var indirectStoreOpCode = typeBeingStored.StindOpCodeFor(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, indirectStoreOpCode, indirectStoreOpCode == OpCodes.Stobj ? Context.TypeResolver.ResolveAny(typeBeingStored.ElementTypeSymbolOf(), ResolveTargetKind.Instruction) : null); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, indirectStoreOpCode, indirectStoreOpCode == OpCodes.Stobj ? Context.TypeResolver.Resolve(typeBeingStored.ElementTypeSymbolOf(), ResolveTargetKind.Instruction) : null); } private void PropertyAssignment(IdentifierNameSyntax node, IPropertySymbol property) @@ -245,7 +245,7 @@ private void FieldAssignment(IFieldSymbol field, IdentifierNameSyntax name) field.EnsureFieldExists(Context, name); var fieldReference = field.FieldResolverExpression(Context); - MemberAssignment(field.Type, field.RefKind, fieldReference, field.StoreOpCodeForFieldAccess()); + MemberAssignment(field.Type, field.RefKind, fieldReference.AsToken(), field.StoreOpCodeForFieldAccess()); } private void LocalVariableAssignment(ILocalSymbol localVariable) @@ -266,10 +266,10 @@ private void MemberAssignment(ITypeSymbol memberType, RefKind memberRefKind, Def { throw new InvalidOperationException("Invalid definition variable"); } - MemberAssignment(memberType, memberRefKind, memberDefinitionVariable.VariableName, storeOpCode); + MemberAssignment(memberType, memberRefKind, memberDefinitionVariable.VariableName.AsLocalVariable(), storeOpCode); } - private void MemberAssignment(ITypeSymbol memberType, RefKind memberRefKind, string memberReference, OpCode storeOpCode) + private void MemberAssignment(ITypeSymbol memberType, RefKind memberRefKind, TOperand memberReference, OpCode storeOpCode) { if (NeedsIndirectStore(memberType, memberRefKind)) { @@ -277,7 +277,7 @@ private void MemberAssignment(ITypeSymbol memberType, RefKind memberRefKind, str } else { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, storeOpCode, memberReference.AsToken()); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, storeOpCode, memberReference); } } diff --git a/Cecilifier.Core/AST/BinaryOperatorHandler.cs b/Cecilifier.Core/AST/BinaryOperatorHandler.cs index c7ad82ed..880eab5f 100644 --- a/Cecilifier.Core/AST/BinaryOperatorHandler.cs +++ b/Cecilifier.Core/AST/BinaryOperatorHandler.cs @@ -10,11 +10,11 @@ namespace Cecilifier.Core.AST; internal sealed class BinaryOperatorHandler { - private readonly Action? _handler; - private readonly Action? _rawHandler; + private readonly Action? _handler; + private readonly Action? _rawHandler; private readonly bool _visitRightOperand; - public void Process(IVisitorContext context, string ilVar, BinaryExpressionSyntax binaryExpression, ExpressionVisitor visitor) + public void Process(IVisitorContext context, IlContext ilVar, BinaryExpressionSyntax binaryExpression, ExpressionVisitor visitor) { if (_handler != null) { @@ -42,7 +42,7 @@ void ThrowIfVisitorIsNull() } } - public void ProcessRaw(IVisitorContext context, string ilVar, ExpressionSyntax left, ExpressionSyntax right) + public void ProcessRaw(IVisitorContext context, IlContext ilVar, ExpressionSyntax left, ExpressionSyntax right) { if (_rawHandler == null) throw new InvalidOperationException("The constructor taking two ITypeSymbols should be used to initialize this instance."); @@ -54,17 +54,17 @@ public void ProcessRaw(IVisitorContext context, string ilVar, ExpressionSyntax l context.SemanticModel.GetTypeInfo(right).Type); } - public BinaryOperatorHandler(Action handler) + public BinaryOperatorHandler(Action handler) { _handler = handler; _visitRightOperand = true; } - private BinaryOperatorHandler(Action action, bool visitRightOperand) + private BinaryOperatorHandler(Action action, bool visitRightOperand) { _rawHandler = action!; _visitRightOperand = visitRightOperand; } - public static BinaryOperatorHandler Raw(Action action, bool visitRightOperand = true) => new(action, visitRightOperand); + public static BinaryOperatorHandler Raw(Action action, bool visitRightOperand = true) => new(action, visitRightOperand); } diff --git a/Cecilifier.Core/AST/CecilExpressionFactory.cs b/Cecilifier.Core/AST/CecilExpressionFactory.cs index 648a297b..65906237 100644 --- a/Cecilifier.Core/AST/CecilExpressionFactory.cs +++ b/Cecilifier.Core/AST/CecilExpressionFactory.cs @@ -6,7 +6,7 @@ namespace Cecilifier.Core.AST; internal static class CecilExpressionFactory { - public static void EmitThrow(IVisitorContext context, string ilVar, ExpressionSyntax expression) + public static void EmitThrow(IVisitorContext context, IlContext ilVar, ExpressionSyntax expression) { _ = LineInformationTracker.Track(context, expression); ExpressionVisitor.Visit(context, ilVar, expression); diff --git a/Cecilifier.Core/AST/CollectionExpressionProcessor.cs b/Cecilifier.Core/AST/CollectionExpressionProcessor.cs index c3e403ad..e51d49c5 100644 --- a/Cecilifier.Core/AST/CollectionExpressionProcessor.cs +++ b/Cecilifier.Core/AST/CollectionExpressionProcessor.cs @@ -65,22 +65,20 @@ private static void HandleAssignmentToSpan(ExpressionVisitor visitor, Collection var currentMethodVar = context.DefinitionVariables.GetLastOf(VariableMemberKind.Method).VariableName; var inlineArrayElementType = spanTypeSymbol.TypeArguments[0]; - var inlineArrayTypeVar = inlineArrayVar.MakeGenericInstanceType(context.TypeResolver.ResolveAny(inlineArrayElementType, ResolveTargetKind.Instruction)); - var inlineArrayTypeVarForLocal = inlineArrayVar.MakeGenericInstanceType(context.TypeResolver.ResolveAny(inlineArrayElementType, ResolveTargetKind.LocalVariable)); + var inlineArrayTypeVar = inlineArrayVar.MakeGenericInstanceType(context.TypeResolver.Resolve(inlineArrayElementType, ResolveTargetKind.Instruction)); + var inlineArrayTypeVarForLocal = inlineArrayVar.MakeGenericInstanceType(context.TypeResolver.Resolve(inlineArrayElementType, ResolveTargetKind.LocalVariable)); var inlineArrayLocalVar = context.ApiDefinitionsFactory.LocalVariable(context, "buffer", currentMethodVar, inlineArrayTypeVarForLocal).VariableName; // Initializes the inline array context.ApiDriver.WriteCilInstruction(context, visitor.ILVariable, OpCodes.Ldloca_S, inlineArrayLocalVar); context.ApiDriver.WriteCilInstruction(context, visitor.ILVariable, OpCodes.Initobj, inlineArrayTypeVar); - var openInlineArrayElementRef = PrivateImplementationDetailsGenerator - .GetOrEmmitInlineArrayElementRefMethod(context); - var inlineArrayElementRefMethodVar = openInlineArrayElementRef - .VariableName - .MakeGenericInstanceMethod(context, openInlineArrayElementRef.MemberName, [$"{inlineArrayLocalVar}.VariableType", context.TypeResolver.ResolveAny(spanTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference)]); + var openInlineArrayElementRef = PrivateImplementationDetailsGenerator.GetOrEmmitInlineArrayElementRefMethod(context); + IReadOnlyList resolvedTypeArguments = [$"{inlineArrayLocalVar}.VariableType", context.TypeResolver.Resolve(spanTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference)]; + var inlineArrayElementRefMethodVar = context.MemberResolver.MakeGeneticInstanceMethod(openInlineArrayElementRef.VariableName, openInlineArrayElementRef.MemberName, resolvedTypeArguments); var storeOpCode = inlineArrayElementType.StindOpCodeFor(); - var targetElementType = storeOpCode == OpCodes.Stobj ? context.TypeResolver.ResolveAny(inlineArrayElementType, ResolveTargetKind.Instruction) : null; // Stobj expects the type of the object being stored. + var targetElementType = storeOpCode == OpCodes.Stobj ? context.TypeResolver.Resolve(inlineArrayElementType, ResolveTargetKind.Instruction) : null; // Stobj expects the type of the object being stored. var collectionExpressionOperation = context.SemanticModel.GetOperation(node).EnsureNotNull(); var index = 0; foreach (var element in node.Elements) @@ -96,9 +94,8 @@ private static void HandleAssignmentToSpan(ExpressionVisitor visitor, Collection // convert the initialized InlineArray to a span and put it in the stack. var openInlineArrayAsSpanVar = PrivateImplementationDetailsGenerator.GetOrEmmitInlineArrayAsSpanMethod(context); - var inlineArrayAsSpanMethodVar = openInlineArrayAsSpanVar - .VariableName - .MakeGenericInstanceMethod(context, openInlineArrayAsSpanVar.MemberName, [$"{inlineArrayLocalVar}.VariableType", context.TypeResolver.ResolveAny(spanTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference)]); + IReadOnlyList typeArguments = [$"{inlineArrayLocalVar}.VariableType", context.TypeResolver.Resolve(spanTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference)]; + var inlineArrayAsSpanMethodVar = context.MemberResolver.MakeGeneticInstanceMethod(openInlineArrayAsSpanVar.VariableName, openInlineArrayAsSpanVar.MemberName, typeArguments); context.ApiDriver.WriteCilInstruction(context, visitor.ILVariable, OpCodes.Ldloca_S, inlineArrayLocalVar); context.ApiDriver.WriteCilInstruction(context, visitor.ILVariable, OpCodes.Ldc_I4, node.Elements.Count); context.ApiDriver.WriteCilInstruction(context, visitor.ILVariable, OpCodes.Call, inlineArrayAsSpanMethodVar); @@ -112,7 +109,7 @@ private static ResolvedType GetOrEmitSyntheticInlineArrayFor(CollectionExpressio private static void HandleAssignmentToArray(ExpressionVisitor visitor, CollectionExpressionSyntax node, IArrayTypeSymbol arrayTypeSymbol) { visitor.Context.ApiDriver.WriteCilInstruction(visitor.Context, visitor.ILVariable, OpCodes.Ldc_I4, node.Elements.Count); - visitor.Context.ApiDriver.WriteCilInstruction(visitor.Context, visitor.ILVariable, OpCodes.Newarr, visitor.Context.TypeResolver.ResolveAny(arrayTypeSymbol.ElementType, ResolveTargetKind.Instruction).AsToken()); + visitor.Context.ApiDriver.WriteCilInstruction(visitor.Context, visitor.ILVariable, OpCodes.Newarr, visitor.Context.TypeResolver.Resolve(arrayTypeSymbol.ElementType, ResolveTargetKind.Instruction).AsToken()); if (PrivateImplementationDetailsGenerator.IsApplicableTo(node, visitor.Context)) ArrayInitializationProcessor.InitializeOptimized(visitor, arrayTypeSymbol.ElementType, node.Elements); diff --git a/Cecilifier.Core/AST/CompilationUnitVisitor.cs b/Cecilifier.Core/AST/CompilationUnitVisitor.cs index d013bd70..dd9f0e2b 100644 --- a/Cecilifier.Core/AST/CompilationUnitVisitor.cs +++ b/Cecilifier.Core/AST/CompilationUnitVisitor.cs @@ -18,7 +18,7 @@ internal CompilationUnitVisitor(IVisitorContext ctx) : base(ctx) public override void VisitCompilationUnit(CompilationUnitSyntax node) { - HandleAttributesInMemberDeclaration(node.AttributeLists, "assembly", VariableMemberKind.None); // There's no enum member for assembly.. for now we use None. + HandleAttributesInMemberDeclaration("assembly", node.AttributeLists, "assembly", VariableMemberKind.None); // There's no enum member for assembly.. for now we use None. VisitDelegates(); VisitDeclaredTypesSortedByDependencies(); diff --git a/Cecilifier.Core/AST/ConstructorDeclarationVisitor.cs b/Cecilifier.Core/AST/ConstructorDeclarationVisitor.cs index aa9ce421..d2832a9a 100644 --- a/Cecilifier.Core/AST/ConstructorDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/ConstructorDeclarationVisitor.cs @@ -36,7 +36,7 @@ public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax no private void HandleStaticConstructor(ConstructorDeclarationSyntax node) { var variableName = Context.Naming.Constructor(node.ResolveDeclaringType(), true); - ProcessMethodDeclaration(node, variableName, Constants.Cecil.StaticConstructorName, $".{Constants.Cecil.StaticConstructorName}", false, ctorVar => + ProcessMethodDeclaration(node, variableName, Constants.Cecil.StaticConstructorName, $".{Constants.Cecil.StaticConstructorName}", ctorVar => { var declaringType = node.Parent.ResolveDeclaringType(); ProcessFieldInitialization(declaringType, ilVar, true); @@ -51,7 +51,7 @@ private void HandleInstanceConstructor(ConstructorDeclarationSyntax node) var callBaseMethod = base.VisitConstructorDeclaration; var ctorVariable = Context.Naming.Constructor(declaringType, false); - ProcessMethodDeclaration(node, ctorVariable, "ctor", ".ctor", false, ctorVar => + ProcessMethodDeclaration(node, ctorVariable, "ctor", ".ctor", ctorVar => { if (node.Initializer == null || node.Initializer.IsKind(SyntaxKind.BaseConstructorInitializer)) { @@ -146,7 +146,7 @@ private void DefaultCtorInjector(string typeDefVar, string normalizedTypeName, s private IlContext AddOrUpdateParameterlessCtorDefinition(string typeName, string normalizedTypeName, string typeDefVar, string ctorAccessibility, bool isStatic, string ctorLocalVar) { var ctorName = isStatic ? "cctor" : "ctor"; - var found = Context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(typeName, Utils.ConstructorMethodName(isStatic), [], 0)); + var found = Context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(typeName, Utils.ConstructorMethodName(isStatic), [], [])); if (found.IsValid) { //TODO: This is Cecil specific. Abstract it and add a test in SRM that exercises it @@ -255,7 +255,7 @@ private string ResolveDefaultCtorFor(string typeDefVar, BaseTypeDeclarationSynta { var typeSymbol = Context.GetDeclaredSymbol(type); if (typeSymbol == null) - return Utils.ImportFromMainModule($"TypeHelpers.DefaultCtorFor({typeDefVar}.BaseType)"); + return Context.MemberResolver.ImportReference($"TypeHelpers.DefaultCtorFor({typeDefVar}.BaseType)"); return Context.MemberResolver.ResolveDefaultConstructor(typeSymbol.BaseType, typeDefVar); } diff --git a/Cecilifier.Core/AST/ConstructorInitializerVisitor.cs b/Cecilifier.Core/AST/ConstructorInitializerVisitor.cs index faa3084a..4ce62353 100644 --- a/Cecilifier.Core/AST/ConstructorInitializerVisitor.cs +++ b/Cecilifier.Core/AST/ConstructorInitializerVisitor.cs @@ -1,6 +1,4 @@ using System.Reflection.Emit; -using Cecilifier.Core.ApiDriver; -using Cecilifier.Core.ApiDriver.Handles; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Cecilifier.Core.Extensions; @@ -9,9 +7,9 @@ namespace Cecilifier.Core.AST { internal class ConstructorInitializerVisitor : SyntaxWalkerBase { - private readonly string ilVar; + private readonly IlContext ilVar; - internal ConstructorInitializerVisitor(IVisitorContext ctx, string ilVar) : base(ctx) + internal ConstructorInitializerVisitor(IVisitorContext ctx, IlContext ilVar) : base(ctx) { this.ilVar = ilVar; } diff --git a/Cecilifier.Core/AST/ConversionOperatorDeclarationVisitor.cs b/Cecilifier.Core/AST/ConversionOperatorDeclarationVisitor.cs index 82310abe..780774a0 100644 --- a/Cecilifier.Core/AST/ConversionOperatorDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/ConversionOperatorDeclarationVisitor.cs @@ -24,7 +24,6 @@ public override void VisitConversionOperatorDeclaration(ConversionOperatorDeclar Context.Naming.MethodDeclaration(node), "operator", operatorMethodName, - false, _ => base.VisitConversionOperatorDeclaration(node)); } @@ -38,7 +37,6 @@ public override void VisitOperatorDeclaration(OperatorDeclarationSyntax node) Context.Naming.MethodDeclaration(node), "operator", declaredSymbol.Name, - false, _ => base.VisitOperatorDeclaration(node)); } diff --git a/Cecilifier.Core/AST/ElementAccessExpressionWithRangeArgumentVisitor.cs b/Cecilifier.Core/AST/ElementAccessExpressionWithRangeArgumentVisitor.cs index 720738a7..4f9c9ebd 100644 --- a/Cecilifier.Core/AST/ElementAccessExpressionWithRangeArgumentVisitor.cs +++ b/Cecilifier.Core/AST/ElementAccessExpressionWithRangeArgumentVisitor.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Reflection.Emit; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.Handles; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -8,12 +7,11 @@ using Cecilifier.Core.Mappings; using Cecilifier.Core.Misc; - namespace Cecilifier.Core.AST; internal class ElementAccessExpressionWithRangeArgumentVisitor : SyntaxWalkerBase { - internal ElementAccessExpressionWithRangeArgumentVisitor(IVisitorContext context, string ilVar, ExpressionVisitor expressionVisitor, bool targetAlreadyLoaded = false) : base(context) + internal ElementAccessExpressionWithRangeArgumentVisitor(IVisitorContext context, IlContext ilVar, ExpressionVisitor expressionVisitor, bool targetAlreadyLoaded = false) : base(context) { _expressionVisitor = expressionVisitor; _targetAlreadyLoaded = targetAlreadyLoaded; @@ -29,7 +27,7 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax var elementAccessExpressionType = Context.SemanticModel.GetTypeInfo(node).Type.EnsureNotNull(); _targetSpanType = elementAccessExpressionType; _spanCopyVariable = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "localSpanCopy", elementAccessExpressionType).VariableName; - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, _spanCopyVariable); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, _spanCopyVariable.AsLocalVariable()); node.ArgumentList.Accept(this); // Visit the argument list with ourselves. @@ -44,22 +42,22 @@ public override void VisitRangeExpression(RangeExpressionSyntax node) using var _ = Context.WithFlag(Constants.ContextFlags.InRangeExpression); // Compute range start index - Utils.EnsureNotNull(node.LeftOperand).Accept(_expressionVisitor); + node.LeftOperand.EnsureNotNull().Accept(_expressionVisitor); var startIndexVar = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "startIndex", Context.RoslynTypeSystem.SystemInt32).VariableName; // Compute number of elements to slice // compute range right index. - Utils.EnsureNotNull(node.RightOperand).Accept(_expressionVisitor); + node.RightOperand.EnsureNotNull().Accept(_expressionVisitor); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar.AsLocalVariable()); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Sub); var elementCountVar = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "elementCount", Context.RoslynTypeSystem.SystemInt32).VariableName; - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, elementCountVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, elementCountVar.AsLocalVariable()); } // This will handle usages like s[r] @@ -90,8 +88,8 @@ private void ProcessIndexerExpressionWithRangeAsArgument(ExpressionSyntax node) Context.AddCallToMethod(systemRange.GetMembers().OfType().Single(p => p.Name == "Start").GetMethod, _ilVar, MethodDispatchInformation.MostLikelyVirtual); var indexVar = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "index", systemIndex).VariableName; - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, indexVar); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, spanLengthVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, indexVar.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, spanLengthVar.AsLocalVariable()); Context.AddCallToMethod(systemIndex.GetMembers().OfType().Single(p => p.Name == "GetOffset"), _ilVar, MethodDispatchInformation.MostLikelyVirtual); var startIndexVar = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "startIndex", Context.RoslynTypeSystem.SystemInt32).VariableName; @@ -99,23 +97,23 @@ private void ProcessIndexerExpressionWithRangeAsArgument(ExpressionSyntax node) // Calculate number of elements to slice. Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, rangeVar); Context.AddCallToMethod(systemRange.GetMembers().OfType().Single(p => p.Name == "End").GetMethod, _ilVar, MethodDispatchInformation.MostLikelyVirtual); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Stloc, new CilLocalVariableHandle(indexVar)); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Stloc, indexVar.AsLocalVariable()); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, indexVar); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, spanLengthVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, indexVar.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, spanLengthVar.AsLocalVariable()); Context.AddCallToMethod(systemIndex.GetMembers().OfType().Single(p => p.Name == "GetOffset"), _ilVar, MethodDispatchInformation.MostLikelyVirtual); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar.AsLocalVariable()); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Sub); var elementCountVar = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "elementCount", Context.RoslynTypeSystem.SystemInt32).VariableName; - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, _spanCopyVariable); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, elementCountVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, _spanCopyVariable.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, startIndexVar.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, elementCountVar.AsLocalVariable()); } private readonly ExpressionVisitor _expressionVisitor; private readonly bool _targetAlreadyLoaded; private string _spanCopyVariable; - private readonly string _ilVar; + private readonly IlContext _ilVar; private ITypeSymbol _targetSpanType; // Span in which indexer is being invoked } diff --git a/Cecilifier.Core/AST/EmptyBodyIlContext.cs b/Cecilifier.Core/AST/EmptyBodyIlContext.cs new file mode 100644 index 00000000..3f4711b6 --- /dev/null +++ b/Cecilifier.Core/AST/EmptyBodyIlContext.cs @@ -0,0 +1,14 @@ +using System; + +namespace Cecilifier.Core.AST; + +/// +/// Used for methods without a body (extern, most interface members, etc) +/// +/// Basically it is used to carry around the name of the variable storing the method definition. +/// +public class EmptyBodyIlContext(string relatedMethodVar) : IlContext(string.Empty, relatedMethodVar) +{ + public override void Materialize() => throw new Exception(); + public override string VariableName => throw new Exception(); +} diff --git a/Cecilifier.Core/AST/EnumDeclarationVisitor.cs b/Cecilifier.Core/AST/EnumDeclarationVisitor.cs index d605ef5b..47cecaae 100644 --- a/Cecilifier.Core/AST/EnumDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/EnumDeclarationVisitor.cs @@ -49,10 +49,10 @@ public override void VisitEnumDeclaration(EnumDeclarationSyntax node) //.class private auto ansi MyEnum var fieldVar = Context.Naming.LocalVariable(node); var definitionContext = new MemberDefinitionContext("value__", "value__", fieldVar, enumTypeVariable); - var valueFieldExp = Context.ApiDefinitionsFactory.Field(Context, definitionContext, declaringTypeName, Context.TypeResolver.ResolveAny(Context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.Field), "FieldAttributes.SpecialName | FieldAttributes.RTSpecialName | FieldAttributes.Public", false, false); + var valueFieldExp = Context.ApiDefinitionsFactory.Field(Context, definitionContext, declaringTypeName, Context.TypeResolver.Resolve(Context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.Field), "FieldAttributes.SpecialName | FieldAttributes.RTSpecialName | FieldAttributes.Public", false, false); AddCecilExpressions(Context, valueFieldExp); - HandleAttributesInMemberDeclaration(node.AttributeLists, enumTypeVariable, VariableMemberKind.Type); + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, enumTypeVariable, VariableMemberKind.Type); base.VisitEnumDeclaration(node); } @@ -69,14 +69,15 @@ public override void VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node var enumMemberSymbol = Context.SemanticModel.GetDeclaredSymbol(node).EnsureNotNull(); var fieldVar = Context.Naming.LocalVariable(node); var declaringTypeName = enumMemberSymbol.ContainingSymbol.ToDisplayString(); - //TODO: Consider introducing (or reusing ?) some abstraction so the code can add more information (like, is this a variable name? is this the result of TypeResolver.Resolve() ? etc) - // to allow Api Drivers (the need for this first appeared in SRM) to decide how to encode/emit the code. For now we ignore that this reference is 100% certain to be the type - // definition for the parent enum (and that we have that variable name in enumVarDef) and re-resolve it in a way that it will return a valid reference. - var enumType = Context.TypeResolver.ResolveAny(Context.SemanticModel.GetDeclaredSymbol(node.Parent).EnsureNotNull(), new TypeResolutionContext(ResolveTargetKind.Field, TypeResolutionOptions.IsValueType)); + + // Some Api Drivers have different syntaxes/apis for type resolution depending on the usage of the resolve type (is it used as a field type? parameter? + // method return? etc) so we can't reuse 'enumVarDef' (which represents the resolved enum type encoded to be used in a type definition). + // Re-resolve the enum type in a way that it will return a valid reference to be used as a field type. + var enumType = Context.TypeResolver.Resolve(Context.SemanticModel.GetDeclaredSymbol(node.Parent).EnsureNotNull(), new TypeResolutionContext(ResolveTargetKind.Field, TypeResolutionOptions.IsValueType)); var exp = Context.ApiDefinitionsFactory.Field(Context, new MemberDefinitionContext(node.Identifier.ValueText, fieldVar, enumVarDef.VariableName), declaringTypeName, enumType, "FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.Public | FieldAttributes.HasDefault", false, false, enumMemberValue); AddCecilExpressions(Context, exp); - HandleAttributesInMemberDeclaration(node.AttributeLists, fieldVar, VariableMemberKind.Field); + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, fieldVar, VariableMemberKind.Field); base.VisitEnumMemberDeclaration(node); } diff --git a/Cecilifier.Core/AST/EventDeclarationVisitor.cs b/Cecilifier.Core/AST/EventDeclarationVisitor.cs index a76236ca..3ec82129 100644 --- a/Cecilifier.Core/AST/EventDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/EventDeclarationVisitor.cs @@ -40,7 +40,7 @@ public override void VisitEventDeclaration(EventDeclarationSyntax node) eventDeclaringTypeVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Type); - var eventType = Context.TypeResolver.ResolveAny(eventSymbol.Type, ResolveTargetKind.None); + var eventType = Context.TypeResolver.Resolve(eventSymbol.Type, ResolveTargetKind.TypeReference); var eventAccessorsDefVarMapping = new Dictionary(); foreach (var acc in node.AccessorList.Accessors) { @@ -51,11 +51,11 @@ public override void VisitEventDeclaration(EventDeclarationSyntax node) var methodVar = Context.Naming.SyntheticVariable(acc.Keyword.ValueText, ElementKind.Method); var ilContext = Context.ApiDriver.NewIlContext(Context, acc.Keyword.ValueText, methodVar); var body = Context.ApiDefinitionsFactory.MethodBody(Context, acc.Keyword.ValueText, ilContext, [], []); - var accessorMethodVar = AddAccessor(node, eventSymbol, in ilContext, methodVar, acc.Keyword.ValueText, eventType, body); + var accessorMethodVar = AddAccessor(node, eventSymbol, in ilContext, methodVar, acc.Keyword.ValueText, body); using (Context.DefinitionVariables.WithVariable(accessorMethodVar)) { - StatementVisitor.Visit(Context, ilContext.VariableName, acc.Body); - Context.ApiDriver.WriteCilInstruction(Context, ilContext.VariableName, OpCodes.Ret); + StatementVisitor.Visit(Context, ilContext, acc.Body); + Context.ApiDriver.WriteCilInstruction(Context, ilContext, OpCodes.Ret); } eventAccessorsDefVarMapping[acc.Keyword.ValueText] = methodVar; @@ -63,8 +63,8 @@ public override void VisitEventDeclaration(EventDeclarationSyntax node) Context.WriteNewLine(); Context.WriteComment($"Event: {node.Identifier.Text}"); - var evtDefVar = AddEventDefinition(node, eventDeclaringTypeVar.VariableName, node.Identifier.Text, eventType, eventAccessorsDefVarMapping["add"], eventAccessorsDefVarMapping["remove"]); - HandleAttributesInMemberDeclaration(node.AttributeLists, evtDefVar, VariableMemberKind.None); + var evtDefVar = AddEventDefinition(node, eventSymbol, eventType, eventAccessorsDefVarMapping["add"], eventAccessorsDefVarMapping["remove"]); + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, evtDefVar, VariableMemberKind.None); } // Handles field like events (i.e, no add/remove accessors) @@ -83,7 +83,7 @@ .event [mscorlib]System.EventHandler TheEvent Context.WriteNewLine(); Context.WriteComment($"Event: {node.Declaration.Variables.First().Identifier.Text}"); - var eventSymbol = (IEventSymbol) Context.SemanticModel.GetDeclaredSymbol(node.Declaration.Variables[0]); + var eventSymbol = Context.SemanticModel.GetDeclaredSymbol(node.Declaration.Variables[0]).EnsureNotNull(); eventDeclaringTypeVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Type); @@ -92,19 +92,19 @@ .event [mscorlib]System.EventHandler TheEvent ? string.Empty : AddBackingField(node); // backing field will have same name as the event - var eventType = ResolveType(node.Declaration.Type, ResolveTargetKind.None); - var addAccessorVar = AddAccessor(node, eventSymbol, "add", backingFieldVar, eventType, AddMethodBody); - var removeAccessorVar = AddAccessor(node, eventSymbol, "remove", backingFieldVar, eventType, RemoveMethodBody); + var eventType = ResolveType(node.Declaration.Type, ResolveTargetKind.TypeReference); + var addAccessorVar = AddAccessor(node, eventSymbol, "add", backingFieldVar, AddMethodBody); + var removeAccessorVar = AddAccessor(node, eventSymbol, "remove", backingFieldVar, RemoveMethodBody); - var evtDefVar = AddEventDefinition(node, eventDeclaringTypeVar.VariableName, eventSymbol.Name, eventType, addAccessorVar, removeAccessorVar); - HandleAttributesInMemberDeclaration(node.AttributeLists, evtDefVar, VariableMemberKind.None); + var evtDefVar = AddEventDefinition(node, eventSymbol, eventType, addAccessorVar, removeAccessorVar); + HandleAttributesInMemberDeclaration(eventSymbol.Name, node.AttributeLists, evtDefVar, VariableMemberKind.None); } - private string AddAccessor(EventFieldDeclarationSyntax node, IEventSymbol eventSymbol, string accessorName, string backingFieldVar, ResolvedType eventType, Func> methodBodyFactory) + private string AddAccessor(EventFieldDeclarationSyntax node, IEventSymbol eventSymbol, string accessorName, string backingFieldVar, Func> methodBodyFactory) { var methodVar = Context.Naming.SyntheticVariable(accessorName, ElementKind.Method); var isInterfaceDef = eventSymbol.ContainingType.TypeKind == TypeKind.Interface; - IEnumerable methodBodyExpressions = Array.Empty(); + IEnumerable methodBodyExpressions = []; IlContext ilContext = default; if (!isInterfaceDef) { @@ -112,16 +112,16 @@ private string AddAccessor(EventFieldDeclarationSyntax node, IEventSymbol eventS methodBodyExpressions = methodBodyFactory(node, eventSymbol, accessorName, methodVar, backingFieldVar, ilContext); } - AddAccessor(node, eventSymbol, in ilContext, methodVar, accessorName, eventType, methodBodyExpressions); + AddAccessor(node, eventSymbol, in ilContext, methodVar, accessorName, methodBodyExpressions); if (!isInterfaceDef) { - CreateLocalVarsForEventRegistrationMethods(methodVar, accessorName, in eventType); + CreateLocalVarsForEventRegistrationMethods(methodVar, accessorName, Context.TypeResolver.Resolve(eventSymbol.Type, ResolveTargetKind.LocalVariable)); } return methodVar; } - private MethodDefinitionVariable AddAccessor(MemberDeclarationSyntax node, IEventSymbol eventSymbol, in IlContext ilContext, string methodVar, string accessorName, ResolvedType eventType, IEnumerable methodBodyExpressions) + private MethodDefinitionVariable AddAccessor(MemberDeclarationSyntax node, IEventSymbol eventSymbol, in IlContext ilContext, string methodVar, string accessorName, IEnumerable methodBodyExpressions) { var accessorModifiers = AccessModifiersForEventAccessors(node, eventSymbol.ContainingType); var methodName = $"{accessorName}_{eventSymbol.Name}"; @@ -130,9 +130,9 @@ private MethodDefinitionVariable AddAccessor(MemberDeclarationSyntax node, IEven new BodiedMemberDefinitionContext(methodName, methodVar, eventDeclaringTypeVar.VariableName, eventSymbol.IsStatic ? MemberOptions.Static : MemberOptions.None, ilContext), eventDeclaringTypeVar.MemberName, accessorModifiers, - [ new ParameterSpec("value", eventType, RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = eventSymbol.Type.ToDisplayString() }], + [ new ParameterSpec("value", Context.TypeResolver.Resolve(eventSymbol.Type, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = eventSymbol.Type.ToDisplayString() }], [], - ctx => ctx.TypeResolver.ResolveAny(Context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(Context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), out var eventAccessorMethodVar); AddCecilExpressions(Context, methodExps.Concat(methodBodyExpressions)); @@ -150,15 +150,15 @@ private void CreateLocalVarsForEventRegistrationMethods(string methodVar, string Context.ApiDefinitionsFactory.LocalVariable(Context, Context.Naming.SyntheticVariable(eventAccessorName, ElementKind.LocalVariable), methodVar, eventType); } - private IEnumerable RemoveMethodBody(EventFieldDeclarationSyntax context, IEventSymbol eventSymbol, string accessorName, string removeMethodVar, string backingFieldVar, IlContext ilContext) + private IEnumerable RemoveMethodBody(EventFieldDeclarationSyntax eventSyntax, IEventSymbol eventSymbol, string accessorName, string removeMethodVar, string backingFieldVar, IlContext ilContext) { var isStatic = eventSymbol.IsStatic; var (ldfld, ldflda) = isStatic ? (OpCodes.Ldsfld, OpCodes.Ldsflda) : (OpCodes.Ldfld, OpCodes.Ldflda); var removeMethod = Context.MemberResolver.ResolveMethod(Context.RoslynTypeSystem.ForType().GetMembers("Remove").Single().EnsureNotNull()); - var compareExchangeExps = CompareExchangeMethodResolvingExps(backingFieldVar, out var compExcVar); + var compExcVar = CompareExchangeMethodResolvingExps(eventSymbol.Type); - var fieldVar = Utils.MakeGenericTypeIfAppropriate(Context, eventSymbol, backingFieldVar, eventDeclaringTypeVar.VariableName); + var fieldVar = Utils.MakeGenericTypeIfAppropriate(Context, eventSymbol, eventSymbol.Name) ?? backingFieldVar; // static member access does not have a *this* so simply replace with *Nop* var lgarg_0 = isStatic ? OpCodes.Nop : OpCodes.Ldarg_0; @@ -170,9 +170,9 @@ private IEnumerable RemoveMethodBody(EventFieldDeclarationSyntax context OpCodes.Ldloc_0.WithInstructionMarker("LoopStart"), OpCodes.Stloc_1, OpCodes.Ldloc_1, - OpCodes.Ldarg.WithOperand(isStatic ? "0" : "1"), + OpCodes.Ldarg.WithOperand(isStatic ? 0 : 1), OpCodes.Call.WithOperand(removeMethod.AsToken()), - OpCodes.Castclass.WithOperand(Context.TypeResolver.ResolveAny(eventSymbol.Type, ResolveTargetKind.Instruction).Expression.AsToken()), + OpCodes.Castclass.WithOperand(Context.TypeResolver.Resolve(eventSymbol.Type, ResolveTargetKind.Instruction).Expression.AsToken()), OpCodes.Stloc_2, lgarg_0, ldflda.WithOperand(fieldVar.AsToken()), @@ -187,22 +187,23 @@ private IEnumerable RemoveMethodBody(EventFieldDeclarationSyntax context ]; var bodyExps = Context.ApiDefinitionsFactory.MethodBody(Context, accessorName, ilContext, localVariableTypes, instructions); - return compareExchangeExps.Concat(bodyExps); + return bodyExps; } - private IEnumerable AddMethodBody(EventFieldDeclarationSyntax context, IEventSymbol eventSymbol, string accessorName, string addMethodVar, string backingFieldVar, IlContext ilContext) + private IEnumerable AddMethodBody(EventFieldDeclarationSyntax eventField, IEventSymbol eventSymbol, string accessorName, string addMethodVar, string backingFieldVar, IlContext ilContext) { var isStatic = eventSymbol.IsStatic; var (ldfld, ldflda) = isStatic ? (OpCodes.Ldsfld, OpCodes.Ldsflda) : (OpCodes.Ldfld, OpCodes.Ldflda); var combineOverloads = Context.RoslynTypeSystem.ForType().GetMembers("Combine").OfType(); var combineMethod = Context.MemberResolver.ResolveMethod(combineOverloads.Single(m => m.IsStatic && m.Parameters.Length == 2).EnsureNotNull()); - var compareExchangeExps = CompareExchangeMethodResolvingExps(backingFieldVar, out var compExcVar); + var compExcVar = CompareExchangeMethodResolvingExps(eventSymbol.Type); - var fieldVar = Utils.MakeGenericTypeIfAppropriate(Context, eventSymbol, backingFieldVar, eventDeclaringTypeVar.VariableName); + var fieldVar = Utils.MakeGenericTypeIfAppropriate(Context, eventSymbol, eventSymbol.Name) ?? backingFieldVar; // static member access does not have a *this* so simply replace with *Nop* var lgarg_0 = isStatic ? OpCodes.Nop : OpCodes.Ldarg_0; + ResolvedType[] localVariableTypes = []; InstructionRepresentation[] instructions = [ lgarg_0, @@ -211,9 +212,9 @@ private IEnumerable AddMethodBody(EventFieldDeclarationSyntax context, I OpCodes.Ldloc_0.WithInstructionMarker("LoopStart"), OpCodes.Stloc_1, OpCodes.Ldloc_1, - OpCodes.Ldarg.WithOperand(isStatic ? "0" : "1"), + OpCodes.Ldarg.WithOperand(isStatic ? 0 : 1), OpCodes.Call.WithOperand(combineMethod.AsToken()), - OpCodes.Castclass.WithOperand(Context.TypeResolver.ResolveAny(eventSymbol.Type, ResolveTargetKind.Instruction).Expression.AsToken()), + OpCodes.Castclass.WithOperand(Context.TypeResolver.Resolve(eventSymbol.Type, ResolveTargetKind.Instruction).Expression.AsToken()), OpCodes.Stloc_2, lgarg_0, ldflda.WithOperand(fieldVar.AsToken()), @@ -227,17 +228,18 @@ private IEnumerable AddMethodBody(EventFieldDeclarationSyntax context, I OpCodes.Nop, OpCodes.Ret ]; - var bodyExps = Context.ApiDefinitionsFactory.MethodBody(Context, accessorName, ilContext, localVariableTypes, instructions); - return compareExchangeExps.Concat(bodyExps); + return Context.ApiDefinitionsFactory.MethodBody(Context, accessorName, ilContext, localVariableTypes, instructions); } - private IEnumerable CompareExchangeMethodResolvingExps(string backingFieldVar, out string compExcVar) + private string CompareExchangeMethodResolvingExps(ITypeSymbol typeArgument) { - var openCompExcVar = Context.Naming.MemberReference("openCompExc"); - var exp1 = $"var {openCompExcVar} = {Utils.ImportFromMainModule("typeof(System.Threading.Interlocked).GetMethods().Single(m => m.Name == \"CompareExchange\" && m.IsGenericMethodDefinition)")};"; + var interlockedTypeSymbol = Context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(System.Threading.Interlocked).FullName!).EnsureNotNull(); + var compareExchangeMethodSymbol = interlockedTypeSymbol.GetMembers("CompareExchange").OfType().Single(m => m.IsGenericMethod); + + var closeCompareExchangeMethod = compareExchangeMethodSymbol.Construct(typeArgument); - return new[] { exp1 }.Concat(openCompExcVar.MakeGenericInstanceMethod(Context, "compExp", [$"{backingFieldVar}.FieldType"], out compExcVar)); + return Context.MemberResolver.ResolveMethod(closeCompareExchangeMethod); } private string AddBackingField(EventFieldDeclarationSyntax node) @@ -257,15 +259,18 @@ private string AddBackingField(EventFieldDeclarationSyntax node) return fields.First(); } - //TODO: Remove eventDeclaringTypeVar from this method and use the field? - private string AddEventDefinition(MemberDeclarationSyntax eventFieldDeclaration, string eventDeclaringTypeVar, string eventName, ResolvedType eventType, string addAccessor, string removeAccessor) + private string AddEventDefinition(MemberDeclarationSyntax eventFieldDeclaration, IEventSymbol eventSymbol, ResolvedType eventType, string addAccessorVariable, string removeAccessorVariable) { var evtDefVar = Context.Naming.EventDeclaration(eventFieldDeclaration); - WriteCecilExpression(Context, $"var {evtDefVar} = new EventDefinition(\"{eventName}\", EventAttributes.None, {eventType.Expression});"); - WriteCecilExpression(Context, $"{evtDefVar}.AddMethod = {addAccessor};"); - WriteCecilExpression(Context, $"{evtDefVar}.RemoveMethod = {removeAccessor};"); - WriteCecilExpression(Context, $"{eventDeclaringTypeVar}.Events.Add({evtDefVar});"); - + var exps = Context.ApiDefinitionsFactory.Event( + Context, + new BodiedMemberDefinitionContext(eventSymbol.Name, eventSymbol.Name.ToValidIdentifier(), evtDefVar, eventDeclaringTypeVar.VariableName, MemberOptions.None, IlContext.None), + eventSymbol.ContainingType.ToDisplayString(), + eventType, + addAccessorVariable, + removeAccessorVariable); + Context.Generate(exps); + return evtDefVar; } diff --git a/Cecilifier.Core/AST/ExpressionVisitor.Stackalloc.cs b/Cecilifier.Core/AST/ExpressionVisitor.Stackalloc.cs index f5a90b83..54a189d6 100644 --- a/Cecilifier.Core/AST/ExpressionVisitor.Stackalloc.cs +++ b/Cecilifier.Core/AST/ExpressionVisitor.Stackalloc.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Threading; using System.Reflection.Emit; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.Handles; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -31,7 +30,7 @@ public override void VisitImplicitStackAllocArrayCreationExpression(ImplicitStac Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4, node.Initializer.EnsureNotNull().Expressions.Count); var stackallocSpanAssignmentTracker = new StackallocSpanAssignmentTracker(node, Context); - var resolvedArrayElementType = Context.TypeResolver.ResolveAny(arrayElementType, ResolveTargetKind.None); + var resolvedArrayElementType = Context.TypeResolver.Resolve(arrayElementType, ResolveTargetKind.None); CalculateLengthInBytesAndEmitLocalloc(stackallocSpanAssignmentTracker, null, resolvedArrayElementType, false); ProcessStackAllocInitializer(node.Initializer); @@ -66,7 +65,7 @@ public override void VisitStackAllocArrayCreationExpression(StackAllocArrayCreat Debug.Assert(arrayType.RankSpecifiers.Count == 1); if (rankNode.IsKind(SyntaxKind.OmittedArraySizeExpression)) { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4, Utils.EnsureNotNull(node.Initializer).Expressions.Count); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4, node.Initializer.EnsureNotNull().Expressions.Count); } var stackallocSpanAssignmentTracker = new StackallocSpanAssignmentTracker(node, Context); @@ -183,15 +182,15 @@ private void CalculateLengthInBytesAndEmitLocalloc(StackallocSpanAssignmentTrack private void EmitNewobjForSpanOfType(ResolvedType spanType) { - var spanInstanceType = Context.TypeResolver.ResolveAny(Context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.None).MakeGenericInstanceType(spanType); + var spanInstanceType = Context.TypeResolver.Resolve(Context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.None).MakeGenericInstanceType(spanType); var spanCtorVar = Context.Naming.SyntheticVariable("spanCtor", ElementKind.LocalVariable); AddCecilExpression($"var {spanCtorVar} = new MethodReference(\".ctor\", {Context.TypeResolver.Bcl.System.Void}, {spanInstanceType}) {{ HasThis = true }};"); var voidPtr = Context.SemanticModel.Compilation.CreatePointerTypeSymbol(Context.RoslynTypeSystem.SystemVoid); - AddCecilExpression($"{spanCtorVar}.Parameters.Add({CecilDefinitionsFactory.ParameterDoesNotHandleParamsKeywordOrDefaultValue("ptr", RefKind.None, Context.TypeResolver.Resolve(voidPtr, ResolveTargetKind.Parameter))});"); - AddCecilExpression($"{spanCtorVar}.Parameters.Add({CecilDefinitionsFactory.ParameterDoesNotHandleParamsKeywordOrDefaultValue("length", RefKind.None, Context.TypeResolver.Bcl.System.Int32)});"); + AddCecilExpression($"{spanCtorVar}.Parameters.Add({CecilDefinitionsFactory.ParameterDoesNotHandleParamsKeywordOrDefaultValue(Context.TypeResolver, "ptr", RefKind.None, Context.TypeResolver.Resolve(voidPtr, ResolveTargetKind.Parameter))});"); + AddCecilExpression($"{spanCtorVar}.Parameters.Add({CecilDefinitionsFactory.ParameterDoesNotHandleParamsKeywordOrDefaultValue(Context.TypeResolver,"length", RefKind.None, Context.TypeResolver.Bcl.System.Int32)});"); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Newobj, Utils.ImportFromMainModule($"{spanCtorVar}")); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Newobj, Context.MemberResolver.ImportReference($"{spanCtorVar}")); } } @@ -247,11 +246,11 @@ internal class StackallocAsArgumentFixer : IStackallocAsArgumentFixer private readonly Queue localVariablesStoringOriginalArguments = new(); private readonly IVisitorContext context; - private readonly string ilVar; + private readonly IlContext ilVar; private LinkedListNode lastLoadTargetOfCallInstruction; private readonly LinkedListNode firstLoadTargetOfCallInstruction; - private StackallocAsArgumentFixer(IVisitorContext context, string ilVar) + private StackallocAsArgumentFixer(IVisitorContext context, IlContext ilVar) { firstLoadTargetOfCallInstruction = context.CurrentLine; this.context = context; @@ -311,7 +310,7 @@ void IStackallocAsArgumentFixer.RestoreCallStackIfRequired() context.MoveLineAfter(callInstruction, context.CurrentLine); } - internal static StackallocPassedAsSpanDisposal TrackPassingStackAllocToSpanArgument(IVisitorContext context, InvocationExpressionSyntax node, string ilVar) + internal static StackallocPassedAsSpanDisposal TrackPassingStackAllocToSpanArgument(IVisitorContext context, InvocationExpressionSyntax node, IlContext ilVar) { // the expression may represent: i) a method invocation, ii) a delegate invocation or iii) nameof() expression. // for the last 2 cases, `method` will be `null` and code will early out. diff --git a/Cecilifier.Core/AST/ExpressionVisitor.cs b/Cecilifier.Core/AST/ExpressionVisitor.cs index 4d5ff900..208049be 100644 --- a/Cecilifier.Core/AST/ExpressionVisitor.cs +++ b/Cecilifier.Core/AST/ExpressionVisitor.cs @@ -27,7 +27,7 @@ internal partial class ExpressionVisitor : SyntaxWalkerBase { private static readonly Dictionary operatorHandlers = new(); - private readonly string ilVar; + private readonly IlContext ilVar; private readonly Stack> callFixList = new Stack>(); // if true, while visiting an AssignmentExpression its left side must not be visited. @@ -116,7 +116,7 @@ static ExpressionVisitor() operatorHandlers[SyntaxKind.IsKeyword] = BinaryOperatorHandler.Raw((ctx, ilVar, _, rightType) => { - ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Isinst, ctx.TypeResolver.ResolveAny(rightType, ResolveTargetKind.Instruction)); + ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Isinst, ctx.TypeResolver.Resolve(rightType, ResolveTargetKind.Instruction)); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Ldnull); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Cgt); }, visitRightOperand: false); // Isinst opcode takes the type to check as a parameter (instead of taking it from the stack) so @@ -131,41 +131,39 @@ static ExpressionVisitor() binaryExpression.Left.Accept(expressionVisitor); var evaluatedLeftVar = ctx.AddLocalVariableToCurrentMethod( "leftValue", - ctx.TypeResolver.ResolveAny(lhsType, ResolveTargetKind.LocalVariable)); + ctx.TypeResolver.Resolve(lhsType, ResolveTargetKind.LocalVariable)); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Stloc, new CilLocalVariableHandle(evaluatedLeftVar.VariableName)); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Ldloca_S, new CilLocalVariableHandle(evaluatedLeftVar.VariableName)); - ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Call, lhsType.GetMembers("get_HasValue").OfType().Single().MethodResolverExpression(ctx)); - - var loadLeftValueInst = ctx.Naming.Instruction("loadLeftValueTarget"); - //TODO: we can't generate like this. We need to call into the driver to generate the instruction - ctx.Generate($"var {loadLeftValueInst} = {ilVar}.Create({OpCodes.Ldloc_S.ConstantName()}, {evaluatedLeftVar.VariableName});"); - ctx.WriteNewLine(); + ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Call, lhsType.GetMembers("get_HasValue").OfType().Single().MethodResolverExpression(ctx).AsToken()); - ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Brtrue_S, loadLeftValueInst); + var loadLeftValueInst = ctx.Naming.Instruction("loadLeftValueTarget"); + ctx.ApiDriver.DefineLabel(ctx, ilVar, loadLeftValueInst); + ctx.ApiDriver.WriteCilBranch(ctx, ilVar, OpCodes.Brtrue_S, loadLeftValueInst); binaryExpression.Right.Accept(expressionVisitor); binaryExpression.Right.InjectRequiredConversions(ctx, ilVar); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Ret); - ctx.Generate($"{ilVar}.Body.Instructions.Add({loadLeftValueInst});"); + + ctx.ApiDriver.MarkLabel(ctx, ilVar, loadLeftValueInst); + ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(evaluatedLeftVar.VariableName)); + ctx.WriteNewLine(); // method handler will add the required ret } else { var returnInstruction = ctx.Naming.Instruction("return"); - ctx.Generate($"var {returnInstruction} = {ilVar}.Create({OpCodes.Nop.ConstantName()});"); - ctx.WriteNewLine(); + ctx.ApiDriver.DefineLabel(ctx, ilVar, returnInstruction); binaryExpression.Left.Accept(expressionVisitor); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Dup); - ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Brtrue_S, returnInstruction); + ctx.ApiDriver.WriteCilBranch(ctx, ilVar, OpCodes.Brtrue_S, returnInstruction); ctx.ApiDriver.WriteCilInstruction(ctx, ilVar, OpCodes.Pop); // removes evaluated LEFT expression from stack binaryExpression.Right.Accept(expressionVisitor); binaryExpression.Right.InjectRequiredConversions(ctx, ilVar); - ctx.Generate($"{ilVar}.Body.Instructions.Add({returnInstruction});"); - ctx.WriteNewLine(); + ctx.ApiDriver.MarkLabel(ctx, ilVar, returnInstruction); } }); } @@ -188,14 +186,14 @@ private static OpCode CompareOpCodeFor(ITypeSymbol left) } } - private ExpressionVisitor(IVisitorContext ctx, string ilVar) : base(ctx) + private ExpressionVisitor(IVisitorContext ctx, IlContext ilVar) : base(ctx) { this.ilVar = ilVar; } - public string ILVariable => ilVar; + public IlContext ILVariable => ilVar; - internal static bool Visit(IVisitorContext ctx, string ilVar, SyntaxNode node) + internal static bool Visit(IVisitorContext ctx, IlContext ilVar, SyntaxNode node) { if (node == null) { @@ -208,7 +206,7 @@ internal static bool Visit(IVisitorContext ctx, string ilVar, SyntaxNode node) return ev.skipLeftSideVisitingInAssignment; } - internal static bool VisitAndPopIfNotConsumed(IVisitorContext ctx, string ilVar, ExpressionSyntax node) + internal static bool VisitAndPopIfNotConsumed(IVisitorContext ctx, IlContext ilVar, ExpressionSyntax node) { var ret = Visit(ctx, ilVar, node); PopIfNotConsumed(ctx, ilVar, node); @@ -321,7 +319,7 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax { if (UsageVisitor.GetInstance(Context).Visit(node.Parent) == UsageKind.CallTarget) { - Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.ResolveAny(elementType, ResolveTargetKind.TypeReference).Expression); + Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.Resolve(elementType, ResolveTargetKind.TypeReference).Expression); return; } @@ -331,7 +329,7 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax // ... otherwise, we need to take the top of the stack (address of the element) and load the actual instance to the stack. var loadOpCode = elementType.LdindOpCodeFor(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadOpCode, loadOpCode == OpCodes.Ldobj ? Context.TypeResolver.ResolveAny(elementType, ResolveTargetKind.Instruction) : null); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadOpCode, loadOpCode == OpCodes.Ldobj ? Context.TypeResolver.Resolve(elementType, ResolveTargetKind.Instruction) : null); return; } @@ -351,11 +349,11 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax } else { - if (HandleLoadAddress(ilVar, targetType, node, OpCodes.Ldelema, Context.TypeResolver.ResolveAny(targetType, ResolveTargetKind.Field).Expression.AsToken())) + if (HandleLoadAddress(ilVar, targetType, node, OpCodes.Ldelema, Context.TypeResolver.Resolve(targetType, ResolveTargetKind.TypeReference).Expression.AsToken())) return; var ldelemOpCodeToUse = targetType.LdelemOpCode(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, ldelemOpCodeToUse, ldelemOpCodeToUse == OpCodes.Ldelem ? Context.TypeResolver.ResolveAny(targetType, ResolveTargetKind.None) : null); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, ldelemOpCodeToUse, ldelemOpCodeToUse == OpCodes.Ldelem ? Context.TypeResolver.Resolve(targetType, ResolveTargetKind.None) : null); } } @@ -373,7 +371,12 @@ public override void VisitAssignmentExpression(AssignmentExpressionSyntax node) return; var visitor = new AssignmentVisitor(Context, ilVar, node); - + + // Force materialization of the variable before capturing 'Context.CurrentLine' since later 'InstructionPrecedingValueToLoad' + // may be used to move lines around, and the code instantiating the representation of the 'il context' + // needs to be taken into account. Failing to materialize the code here may result in the 'il variable' to be used + // before its declaration. + ilVar.Materialize(); visitor.InstructionPrecedingValueToLoad = Context.CurrentLine; Visit(node.Right); node.Right.InjectRequiredConversions(Context, ilVar); @@ -474,12 +477,12 @@ public override void VisitDeclarationExpression(DeclarationExpressionSyntax node { var localSymbol = Context.SemanticModel.GetSymbolInfo(node).Symbol.EnsureNotNull(); var designation = ((SingleVariableDesignationSyntax) node.Designation); - var resolvedOutArgType = Context.TypeResolver.ResolveAny(localSymbol.Type, ResolveTargetKind.LocalVariable); + var resolvedOutArgType = Context.TypeResolver.Resolve(localSymbol.Type, ResolveTargetKind.LocalVariable); DefinitionVariable methodVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); var outLocalName = Context.ApiDefinitionsFactory.LocalVariable(Context, designation.Identifier.Text, methodVar.VariableName, resolvedOutArgType).VariableName; - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, outLocalName); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, outLocalName.AsToken()); } base.VisitDeclarationExpression(node); @@ -523,8 +526,8 @@ public override void VisitConditionalAccessExpression(ConditionalAccessExpressio // code to handle null case var currentMethodVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); var expressionTypeInfo = Context.SemanticModel.GetTypeInfo(node); - var resolvedConcreteNullableType = Context.TypeResolver.ResolveAny(expressionTypeInfo.Type!, ResolveTargetKind.None); - var resolvedConcreteNullableTypeForLocal = Context.TypeResolver.ResolveAny(expressionTypeInfo.Type!, ResolveTargetKind.LocalVariable); + var resolvedConcreteNullableType = Context.TypeResolver.Resolve(expressionTypeInfo.Type!, ResolveTargetKind.None); + var resolvedConcreteNullableTypeForLocal = Context.TypeResolver.Resolve(expressionTypeInfo.Type!, ResolveTargetKind.LocalVariable); var tempNullableVar = Context.ApiDefinitionsFactory.LocalVariable(Context, "nullable", currentMethodVar.VariableName, resolvedConcreteNullableTypeForLocal).VariableName; Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, tempNullableVar); @@ -819,6 +822,7 @@ public override void VisitCastExpression(CastExpressionSyntax node) else if (conversion.IsBoxing || conversion.IsImplicit && conversion.IsReference && castSourceType.TypeKind == TypeKind.TypeParameter) { AddCilInstruction(ilVar, OpCodes.Box, castSourceType); + } else if (conversion.IsUnboxing) { @@ -908,7 +912,7 @@ public override void VisitIsPatternExpression(IsPatternExpressionSyntax node) var t = Context.SemanticModel.GetTypeInfo(node.Expression).Type.EnsureNotNull(); if (t.TypeKind == TypeKind.TypeParameter) { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.ResolveAny(t, ResolveTargetKind.None)); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.Resolve(t, ResolveTargetKind.None)); } node.Pattern.Accept(this); } @@ -917,9 +921,9 @@ public override void VisitDeclarationPattern(DeclarationPatternSyntax node) { using var _ = LineInformationTracker.Track(Context, node); - var varType = Context.TypeResolver.ResolveAny(Context.SemanticModel.GetTypeInfo(node.Type).Type.EnsureNotNull(), ResolveTargetKind.None); + var varType = Context.TypeResolver.Resolve(Context.SemanticModel.GetTypeInfo(node.Type).Type.EnsureNotNull(), ResolveTargetKind.None); var localVarName = ((SingleVariableDesignationSyntax) node.Designation).Identifier.ValueText; - var varTypeForLocal = Context.TypeResolver.ResolveAny(Context.SemanticModel.GetTypeInfo(node.Type).Type.EnsureNotNull(), ResolveTargetKind.LocalVariable); + var varTypeForLocal = Context.TypeResolver.Resolve(Context.SemanticModel.GetTypeInfo(node.Type).Type.EnsureNotNull(), ResolveTargetKind.LocalVariable); var localVar = Context.AddLocalVariableToCurrentMethod(localVarName, varTypeForLocal).VariableName; Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Isinst, varType); @@ -933,9 +937,9 @@ public override void VisitRecursivePattern(RecursivePatternSyntax node) { using var _ = LineInformationTracker.Track(Context, node); - var varType = Context.TypeResolver.ResolveAny(Context.SemanticModel.GetTypeInfo(node.Type!).Type.EnsureNotNull(), ResolveTargetKind.None); + var varType = Context.TypeResolver.Resolve(Context.SemanticModel.GetTypeInfo(node.Type!).Type.EnsureNotNull(), ResolveTargetKind.None); var localVarName = LocalVariableNameOrDefault(node, "tmp"); - var varTypeForLocalVariable = Context.TypeResolver.ResolveAny(Context.SemanticModel.GetTypeInfo(node.Type!).Type.EnsureNotNull(), ResolveTargetKind.LocalVariable); + var varTypeForLocalVariable = Context.TypeResolver.Resolve(Context.SemanticModel.GetTypeInfo(node.Type!).Type.EnsureNotNull(), ResolveTargetKind.LocalVariable); var localVar = Context.AddLocalVariableToCurrentMethod(localVarName, varTypeForLocalVariable).VariableName; var typeDoesNotMatchVar = CreateCilInstruction(ilVar, OpCodes.Ldc_I4_0); @@ -964,11 +968,11 @@ public override void VisitRecursivePattern(RecursivePatternSyntax node) } } - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4_1); // if the execution (at runtime) reaches this point it means the - // pattern is a match + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4_1); // if the execution (at runtime) reaches this point, it means the pattern is a match Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Br_S, typeMatchesVar); - AddCecilExpression($"{ilVar}.Append({typeDoesNotMatchVar});"); - AddCecilExpression($"{ilVar}.Append({typeMatchesVar});"); + //TODO: Mono.Cecil specific code... abstract it to use ApiDriver apis. + AddCecilExpression($"{ilVar.VariableName}.Append({typeDoesNotMatchVar});"); + AddCecilExpression($"{ilVar.VariableName}.Append({typeMatchesVar});"); string LocalVariableNameOrDefault(RecursivePatternSyntax toCheck, string defaultValue) { @@ -1036,7 +1040,7 @@ private bool TryProcessTypeParameterInstantiation(BaseObjectCreationExpressionSy .MakeGenericInstanceMethod( Context, "CreateInstance", - [Context.TypeResolver.ResolveAny(instantiatedType, ResolveTargetKind.None)], + [Context.TypeResolver.Resolve(instantiatedType, ResolveTargetKind.None)], out var closedCreateInstanceMethod); Context.Generate(exps); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Call, closedCreateInstanceMethod); @@ -1184,7 +1188,7 @@ private void ProcessIndexerExpression(PrefixUnaryExpressionSyntax node) private void ProcessIndexerExpressionInElementAccessExpression(PrefixUnaryExpressionSyntax indexerExpression, ElementAccessExpressionSyntax elementAccessExpressionSyntax) { - Utils.EnsureNotNull(elementAccessExpressionSyntax); + elementAccessExpressionSyntax.EnsureNotNull(); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Dup); // Duplicate the target of the element access expression @@ -1245,7 +1249,7 @@ private void HandleLambdaExpression(LambdaExpressionSyntax node) } Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldnull); - CecilDefinitionsFactory.InstantiateDelegate(Context, ilVar, Context.GetTypeInfo(node).ConvertedType!, syntheticMethodVariable.VariableName, new StaticDelegateCacheContext + CodeGenerationHelpers.InstantiateDelegate(Context, ilVar, Context.GetTypeInfo(node).ConvertedType!, syntheticMethodVariable.VariableName, new StaticDelegateCacheContext { IsStaticDelegate = false }); @@ -1302,7 +1306,7 @@ private void StoreTopOfStackInLocalVariableAndLoad(ExpressionSyntax expressionSy if (parentMae != null && SymbolEqualityComparer.Default.Equals(Context.SemanticModel.GetSymbolInfo(parentMae).Symbol!.ContainingType, Context.RoslynTypeSystem.SystemValueType)) { // it is a reference on a value type to a method defined in System.ValueType (GetHashCode(), ToString(), etc) - Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.ResolveAny(type, ResolveTargetKind.None).Expression); + Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.Resolve(type, ResolveTargetKind.TypeReference).Expression); } bool RequiresAddressOfValue() => IsObjectCreationExpressionUsedAsInParameter(expressionSyntax) || expressionSyntax.Parent!.FirstAncestorOrSelf(c => !c.IsKind(SyntaxKind.ParenthesizedExpression)).IsKind(SyntaxKind.SimpleMemberAccessExpression); @@ -1418,7 +1422,7 @@ private void ProcessMethodReference(SimpleNameSyntax node, IMethodSymbol method) //IL_0002: ldftn string Test::M(int32) //IL_0008: newobj instance void class [System.Private.CoreLib]System.Func`2::.ctor(object, native int) Context.EnsureForwardedMethod(method.OverriddenMethod ?? method.OriginalDefinition); - CecilDefinitionsFactory.InstantiateDelegate(Context, ilVar, delegateType, method.MethodResolverExpression(Context), new StaticDelegateCacheContext() + InstantiateDelegate(Context, ilVar, delegateType, method.MethodResolverExpression(Context), new StaticDelegateCacheContext() { IsStaticDelegate = method.IsStatic || method.MethodKind == MethodKind.LocalFunction, Method = method, @@ -1493,7 +1497,9 @@ private void HandleMethodInvocation(SyntaxNode target, ArgumentListSyntax args, } else { + ilVar.Materialize(); // See comment in VisitAssignmentExpression(). Visit(target); + var callContext = (FirstInstruction: _lastInstructionLoadingTargetOfInvocation, LastInstruction: Context.CurrentLine); StackallocAsArgumentFixer.Current?.MarkEndOfComputedCallTargetBlock(callContext.FirstInstruction); @@ -1566,7 +1572,7 @@ private void HandleIdentifier(SimpleNameSyntax node) break; case SymbolKind.TypeParameter: - Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.ResolveAny((ITypeSymbol) member.Symbol, ResolveTargetKind.None).Expression); + Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.Resolve((ITypeSymbol) member.Symbol, ResolveTargetKind.None).Expression); break; case SymbolKind.Event: @@ -1588,7 +1594,7 @@ private void ProcessImplicitArrayCreationExpression(InitializerExpressionSyntax private void ProcessArrayCreation(ITypeSymbol elementType, InitializerExpressionSyntax? initializer) { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Newarr, Context.TypeResolver.ResolveAny(elementType, ResolveTargetKind.Instruction).AsToken()); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Newarr, Context.TypeResolver.Resolve(elementType, ResolveTargetKind.Instruction).AsToken()); if (PrivateImplementationDetailsGenerator.IsApplicableTo(initializer, Context)) ArrayInitializationProcessor.InitializeOptimized(this, elementType, initializer.Expressions); else @@ -1602,7 +1608,7 @@ string EmitTargetLabel(string relatedToName) return labelVariable; } - private static void PopIfNotConsumed(IVisitorContext ctx, string ilVar, ExpressionSyntax node) + private static void PopIfNotConsumed(IVisitorContext ctx, IlContext ilVar, ExpressionSyntax node) { var nodeType = ctx.GetTypeInfo(node).Type.EnsureNotNull(); if (!node.IsKind(SyntaxKind.SimpleAssignmentExpression) @@ -1613,7 +1619,7 @@ private static void PopIfNotConsumed(IVisitorContext ctx, string ilVar, Expressi } } - private static void HandleModulusExpression(IVisitorContext context, string ilVar, ITypeSymbol lhs, ITypeSymbol rhs) + private static void HandleModulusExpression(IVisitorContext context, IlContext ilVar, ITypeSymbol lhs, ITypeSymbol rhs) { var l = lhs.GetMembers("op_Modulus").OfType().SingleOrDefault(); var r = rhs.GetMembers("op_Modulus").OfType().SingleOrDefault(); diff --git a/Cecilifier.Core/AST/FieldDeclarationVisitor.cs b/Cecilifier.Core/AST/FieldDeclarationVisitor.cs index 5f283984..99d2faa9 100644 --- a/Cecilifier.Core/AST/FieldDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/FieldDeclarationVisitor.cs @@ -55,7 +55,7 @@ private IEnumerable HandleFieldDeclaration(MemberDeclarationSyntax node, var fieldDefVars = new List(variableDeclarationSyntax.Variables.Count); var fieldType = ResolveTypeSymbol(variableDeclarationSyntax.Type); - var fieldAttributes = ModifiersToCecil(modifiers, "Private", MapFieldAttributesFor); + var fieldAttributes = ModifiersAsString(modifiers, "Private", MapFieldAttributesFor); var isByRef = variableDeclarationSyntax.Type is RefTypeSyntax; foreach (var field in variableDeclarationSyntax.Variables) @@ -81,7 +81,7 @@ private IEnumerable HandleFieldDeclaration(MemberDeclarationSyntax node, isByRef, constant.Value.ValueText()); AddCecilExpressions(Context, exps); - HandleAttributesInMemberDeclaration(node.AttributeLists, fieldVar, VariableMemberKind.Field); + HandleAttributesInMemberDeclaration(fieldSymbol.Name, node.AttributeLists, fieldVar, VariableMemberKind.Field); } return fieldDefVars; diff --git a/Cecilifier.Core/AST/GlobalStatementHandler.cs b/Cecilifier.Core/AST/GlobalStatementHandler.cs index 1752bae5..ca8b9315 100644 --- a/Cecilifier.Core/AST/GlobalStatementHandler.cs +++ b/Cecilifier.Core/AST/GlobalStatementHandler.cs @@ -20,7 +20,7 @@ internal GlobalStatementHandler(IVisitorContext context, GlobalStatementSyntax f var hasReturnStatement = firstGlobalStatement.Parent!.DescendantNodes().Any(node => node.IsKind(SyntaxKind.ReturnStatement)); - var typeModifiers = CecilDefinitionsFactory.DefaultTypeAttributeFor(TypeKind.Class, false).AppendModifier("TypeAttributes.NotPublic | TypeAttributes.AutoLayout"); + var typeModifiers = CecilDefinitionsFactory.DefaultTypeAttributeFor(TypeKind.Class, false).AppendEnumFlag("TypeAttributes.NotPublic | TypeAttributes.AutoLayout"); typeVar = context.Naming.Type("topLevelStatements", ElementKind.Class); var typeExps = context.ApiDefinitionsFactory.Type( context, @@ -44,29 +44,28 @@ internal GlobalStatementHandler(IVisitorContext context, GlobalStatementSyntax f null); methodVar = context.Naming.SyntheticVariable("topLevelMain", ElementKind.Method); - var ilContext = context.ApiDriver.NewIlContext(context, "topLevelMain", methodVar); + ilVar = context.ApiDriver.NewIlContext(context, "topLevelMain", methodVar); var methodExps = context.ApiDefinitionsFactory.Method( context, - new BodiedMemberDefinitionContext("
$", "programMain", methodVar, typeVar, MemberOptions.Static, ilContext), + new BodiedMemberDefinitionContext("
$", "programMain", methodVar, typeVar, MemberOptions.Static, ilVar), "Program", "MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static", [new ParameterSpec("args", context.TypeResolver.MakeArrayType(context.RoslynTypeSystem.SystemString, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None)], [], - ctx => ctx.TypeResolver.ResolveAny(hasReturnStatement ? context.RoslynTypeSystem.SystemInt32 : context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(hasReturnStatement ? context.RoslynTypeSystem.SystemInt32 : context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), out _); context.Generate(methodExps); - var mainBodyExps = context.ApiDefinitionsFactory.MethodBody(context, "topLevelMain", ilContext, [], []); + var mainBodyExps = context.ApiDefinitionsFactory.MethodBody(context, "topLevelMain", ilVar, [], []); context.Generate(mainBodyExps); - ilVar = ilContext.VariableName; // TODO: (remove) This forces the related ILProcessor variable to be emitted. NonCapturingLambdaProcessor.InjectSyntheticMethodsForNonCapturingLambdas(context, firstGlobalStatement, typeVar); } public bool HandleGlobalStatement(GlobalStatementSyntax node) { using (context.DefinitionVariables.WithCurrent("", "Program", VariableMemberKind.Type, typeVar)) - using (context.DefinitionVariables.WithCurrentMethod("Program", "
$", [], 0, methodVar)) + using (context.DefinitionVariables.WithCurrentMethod("Program", "
$", [], [], methodVar)) { if (node.Statement.IsKind(SyntaxKind.LocalFunctionStatement)) { @@ -103,7 +102,7 @@ bool IsLastGlobalStatement(CompilationUnitSyntax compilation, int index) public string MainMethodDefinitionVariable => methodVar; - private readonly string ilVar; + private readonly IlContext ilVar; private readonly string methodVar; private readonly string typeVar; private readonly IVisitorContext context; diff --git a/Cecilifier.Core/AST/IVisitorContext.cs b/Cecilifier.Core/AST/IVisitorContext.cs index e1792edb..03fd754f 100644 --- a/Cecilifier.Core/AST/IVisitorContext.cs +++ b/Cecilifier.Core/AST/IVisitorContext.cs @@ -50,7 +50,7 @@ public interface IVisitorContext TypeInfo GetTypeInfo(TypeSyntax node); TypeInfo GetTypeInfo(ExpressionSyntax expressionSyntax); - void WriteCilInstructionAfter(string ilVar, OpCode opCode, LinkedListNode after); + void WriteCilInstructionAfter(IlContext ilVar, OpCode opCode, LinkedListNode after); void Generate(CecilifierInterpolatedStringHandler expression); void Generate(string expression); void Generate(IEnumerable expressions); @@ -76,15 +76,3 @@ public interface IVisitorContext #endregion } - -public class IlContext -{ - protected IlContext(string variableName, string relatedMethodVar) => (VariableName, AssociatedMethodVariable) = (variableName, relatedMethodVar); - public static readonly IlContext None = new IlContext(string.Empty, string.Empty); - - //TODO: Remove these implicit operators and fix all usages of il variable names - public static implicit operator IlContext(string variableName) => new(variableName, "N/A"); - public static implicit operator string(IlContext x) => x.VariableName; - public virtual string VariableName { get; } - public string AssociatedMethodVariable { get; } -} diff --git a/Cecilifier.Core/AST/IlContext.cs b/Cecilifier.Core/AST/IlContext.cs new file mode 100644 index 00000000..3cecc4a9 --- /dev/null +++ b/Cecilifier.Core/AST/IlContext.cs @@ -0,0 +1,21 @@ +using System.Diagnostics; + +namespace Cecilifier.Core.AST; + +[DebuggerDisplay("{VariableName} (method: {AssociatedMethodVariable})")] +public class IlContext +{ + protected IlContext(string variableName, string relatedMethodVar) => (VariableName, AssociatedMethodVariable) = (variableName, relatedMethodVar); + public static readonly IlContext None = new(string.Empty, string.Empty); + + /// Ensures that any code that needs to be generated before the variable is used is generated. + /// + /// Note that accessing has the same effect. This method is intended to be used in places + /// where even though no such access exists, materialization of the variable is still needed (in general due to + /// ordering of statements). + /// + public virtual void Materialize() { } + + public virtual string VariableName { get; } + public string AssociatedMethodVariable { get; } +} diff --git a/Cecilifier.Core/AST/InlineArrayProcessor.cs b/Cecilifier.Core/AST/InlineArrayProcessor.cs index b6f3001c..c81b4cc3 100644 --- a/Cecilifier.Core/AST/InlineArrayProcessor.cs +++ b/Cecilifier.Core/AST/InlineArrayProcessor.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -13,7 +14,7 @@ namespace Cecilifier.Core.AST; public class InlineArrayProcessor { - internal static bool HandleInlineArrayConversionToSpan(IVisitorContext context, string ilVar, ITypeSymbol fromType, SyntaxNode fromNode, OpCode opcode, string name, VariableMemberKind memberKind, string parentName = null) + internal static bool HandleInlineArrayConversionToSpan(IVisitorContext context, IlContext ilVar, ITypeSymbol fromType, SyntaxNode fromNode, OpCode opcode, TOperand operand) { int inlineArrayLength = InlineArrayLengthFrom(fromType); if (inlineArrayLength == -1) @@ -27,9 +28,9 @@ internal static bool HandleInlineArrayConversionToSpan(IVisitorContext context, // ldloca.s address of fromNode. // ldci4 fromNode.Length (size of the inline array) - context.ApiDriver.WriteCilInstruction(context, ilVar, opcode, context.DefinitionVariables.GetVariable(name, memberKind, parentName).VariableName); + context.ApiDriver.WriteCilInstruction(context, ilVar, opcode, operand); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldc_I4, inlineArrayLength); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Call, InlineArrayAsSpanMethodFor(context, fromType)); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Call, InlineArrayAsSpanMethodFor(context, fromType).AsToken()); return true; static bool IsNodeAssignedToLocalVariable(IVisitorContext context, SyntaxNode nodeToCheck) @@ -63,7 +64,7 @@ static bool IsNodeUsedToInitializeSpanLocalVariable(IVisitorContext context, Syn return false; var variableDeclaration = (VariableDeclarationSyntax) parent.Parent.Parent!; - var declaredVariableType = ModelExtensions.GetTypeInfo(context.SemanticModel, variableDeclaration.Type); + var declaredVariableType = context.SemanticModel.GetTypeInfo(variableDeclaration.Type); return SymbolEqualityComparer.Default.Equals(declaredVariableType.Type?.OriginalDefinition, context.RoslynTypeSystem.SystemSpan); } @@ -82,7 +83,7 @@ static string InlineArrayAsSpanMethodFor(IVisitorContext context, ITypeSymbol in /// All this method needs to do is to convert the inline array => Span{T} and use the same code that handles /// 'indexing' a Span{T} with ranges. /// - internal static bool TryHandleRangeElementAccess(IVisitorContext context, ExpressionVisitor expressionVisitor, string ilVar, ElementAccessExpressionSyntax elementAccess, out ITypeSymbol elementType) + internal static bool TryHandleRangeElementAccess(IVisitorContext context, ExpressionVisitor expressionVisitor, IlContext ilVar, ElementAccessExpressionSyntax elementAccess, out ITypeSymbol elementType) { elementType = null; if (elementAccess.Expression.IsKind(SyntaxKind.ElementAccessExpression)) @@ -99,7 +100,11 @@ internal static bool TryHandleRangeElementAccess(IVisitorContext context, Expres var memberParentName = storageVariableMemberKind == VariableMemberKind.LocalVariable ? string.Empty : storageSymbol.ContainingSymbol.ToDisplayString(); // Takes the inline array and convert to a Span - HandleInlineArrayConversionToSpan(context, ilVar, inlineArrayType, elementAccess, storageSymbol.LoadAddressOpcodeForMember(), elementAccess.Expression.ToString(), storageVariableMemberKind, memberParentName); + var storageVariableVar = context.DefinitionVariables.GetVariable(elementAccess.Expression.ToString(), storageVariableMemberKind, memberParentName); + storageVariableVar.ThrowIfVariableIsNotValid($"Definition variable for storage {storageSymbol.Name} not found."); + + var details = storageSymbol.LoadAddressDetailsForForMember(); + HandleInlineArrayConversionToSpan(context, ilVar, inlineArrayType, elementAccess, details.OpCode, details.Factory(storageVariableVar.VariableName)); // at this point we have a Span (for the inline array) at the top of the stack so just delegate to the visitor in charge of handling // indexing Span with a range. @@ -110,7 +115,7 @@ internal static bool TryHandleRangeElementAccess(IVisitorContext context, Expres return false; } - internal static bool TryHandleIntIndexElementAccess(IVisitorContext context, string ilVar, ElementAccessExpressionSyntax elementAccess, out ITypeSymbol elementType) + internal static bool TryHandleIntIndexElementAccess(IVisitorContext context, IlContext ilVar, ElementAccessExpressionSyntax elementAccess, out ITypeSymbol elementType) { elementType = null; if (elementAccess.Expression.IsKind(SyntaxKind.ElementAccessExpression)) @@ -133,7 +138,7 @@ internal static bool TryHandleIntIndexElementAccess(IVisitorContext context, str ExpressionVisitor.Visit(context, ilVar, elementAccess.ArgumentList.Arguments[0].Expression); method = InlineArrayElementRefMethodFor(context, inlineArrayType); } - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Call, method); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Call, method.AsToken()); elementType = InlineArrayElementTypeFrom(inlineArrayType); return true; @@ -157,14 +162,12 @@ static string InlineArrayElementRefMethodFor(IVisitorContext context, ITypeSymbo static string PrivateImplementationInlineArrayGenericInstanceMethodFor(IVisitorContext context, DefinitionVariable openGenericTypeVar, ITypeSymbol inlineArrayType) { - var varName = openGenericTypeVar.VariableName.MakeGenericInstanceMethod( - context, - openGenericTypeVar.MemberName, - [ - context.TypeResolver.ResolveAny(inlineArrayType, ResolveTargetKind.None), // TBuffer - context.TypeResolver.ResolveAny(InlineArrayElementTypeFrom(inlineArrayType), ResolveTargetKind.None) // TElement - ]); - return varName; + IReadOnlyList resolvedTypeArguments = + [ + context.TypeResolver.Resolve(inlineArrayType, ResolveTargetKind.GenericTypeArgument), // TBuffer + context.TypeResolver.Resolve(InlineArrayElementTypeFrom(inlineArrayType), ResolveTargetKind.GenericTypeArgument) // TElement + ]; + return context.MemberResolver.MakeGeneticInstanceMethod(openGenericTypeVar.VariableName, openGenericTypeVar.MemberName, resolvedTypeArguments); } private static ITypeSymbol InlineArrayElementTypeFrom(ITypeSymbol inlineArrayType) diff --git a/Cecilifier.Core/AST/InterpolatedStringVisitor.NumberOfPameterDependent.cs b/Cecilifier.Core/AST/InterpolatedStringVisitor.NumberOfPameterDependent.cs index 9be47490..76c9691f 100644 --- a/Cecilifier.Core/AST/InterpolatedStringVisitor.NumberOfPameterDependent.cs +++ b/Cecilifier.Core/AST/InterpolatedStringVisitor.NumberOfPameterDependent.cs @@ -12,7 +12,7 @@ internal class InterpolatedStringUpTo3ArgumentsVisitor : InterpolatedStringVisit { private readonly int _numberOfArguments; - public InterpolatedStringUpTo3ArgumentsVisitor(IVisitorContext context, string ilVar, ExpressionVisitor expressionVisitor, int numberOfArguments) : base(context, ilVar, expressionVisitor) + public InterpolatedStringUpTo3ArgumentsVisitor(IVisitorContext context, IlContext ilVar, ExpressionVisitor expressionVisitor, int numberOfArguments) : base(context, ilVar, expressionVisitor) { _numberOfArguments = numberOfArguments; } @@ -27,7 +27,7 @@ protected override IMethodSymbol GetStringFormatOverloadToCall() internal class InterpolatedStringWithMoreThan3ArgumentsVisitor : InterpolatedStringVisitor { - public InterpolatedStringWithMoreThan3ArgumentsVisitor(IVisitorContext context, string ilVar, ExpressionVisitor expressionVisitor, int numberOfArguments) : base(context, ilVar, expressionVisitor) + public InterpolatedStringWithMoreThan3ArgumentsVisitor(IVisitorContext context, IlContext ilVar, ExpressionVisitor expressionVisitor, int numberOfArguments) : base(context, ilVar, expressionVisitor) { _numberOfArguments = numberOfArguments; } diff --git a/Cecilifier.Core/AST/InterpolatedStringVisitor.cs b/Cecilifier.Core/AST/InterpolatedStringVisitor.cs index 5c30b21d..21be37fb 100644 --- a/Cecilifier.Core/AST/InterpolatedStringVisitor.cs +++ b/Cecilifier.Core/AST/InterpolatedStringVisitor.cs @@ -12,7 +12,7 @@ namespace Cecilifier.Core.AST { internal abstract class InterpolatedStringVisitor : SyntaxWalkerBase { - public static InterpolatedStringVisitor For(InterpolatedStringExpressionSyntax node, IVisitorContext context, string ilVar, ExpressionVisitor expressionVisitor) + public static InterpolatedStringVisitor For(InterpolatedStringExpressionSyntax node, IVisitorContext context, IlContext ilVar, ExpressionVisitor expressionVisitor) { var numberOfArguments = node.Contents.OfType().Count(); return numberOfArguments <= 3 @@ -26,7 +26,7 @@ public static InterpolatedStringVisitor For(InterpolatedStringExpressionSyntax n .GetMembers("Format") .OfType(); - protected InterpolatedStringVisitor(IVisitorContext context, string ilVar, ExpressionVisitor expressionVisitor) : base(context) + protected InterpolatedStringVisitor(IVisitorContext context, IlContext ilVar, ExpressionVisitor expressionVisitor) : base(context) { _ilVar = ilVar; _expressionVisitor = expressionVisitor; @@ -69,8 +69,8 @@ public override void VisitInterpolatedStringText(InterpolatedStringTextSyntax no _computedFormat.Append(node.TextToken.ToFullString()); } - protected readonly string _ilVar; - protected byte _currentParameterIndex = 0; + protected readonly IlContext _ilVar; + protected byte _currentParameterIndex; private readonly StringBuilder _computedFormat = new(); private readonly ExpressionVisitor _expressionVisitor; diff --git a/Cecilifier.Core/AST/MethodDeclarationVisitor.cs b/Cecilifier.Core/AST/MethodDeclarationVisitor.cs index 4f3f8baf..59d3d140 100644 --- a/Cecilifier.Core/AST/MethodDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/MethodDeclarationVisitor.cs @@ -15,7 +15,7 @@ namespace Cecilifier.Core.AST { class MethodDeclarationVisitor : SyntaxWalkerBase { - protected string ilVar; + protected IlContext ilVar; public MethodDeclarationVisitor(IVisitorContext context) : base(context) { @@ -59,14 +59,11 @@ public override void VisitBlock(BlockSyntax node) public override void VisitMethodDeclaration(MethodDeclarationSyntax node) { - var refReturn = node.ReturnType is RefTypeSyntax; - ProcessMethodDeclaration( node, Context.Naming.MethodDeclaration(node), node.Identifier.ValueText, MethodNameOf(node), - refReturn, _ => base.VisitMethodDeclaration(node), node.TypeParameterList?.Parameters.ToArray()); } @@ -83,9 +80,7 @@ public override void VisitParameterList(ParameterListSyntax node) public override void VisitParameter(ParameterSyntax node) { - //TODO: Introduce a way for contexts specify that the related ApiDriver does not need - // to handle ParameterSyntax ? - if (Context.GetType().Name.Contains("SystemReflectionMetadataContext")) + if (!Context.ApiDriver.DriverCapabilities.HasFlag(ApiDriverCapabilities.RequiresExplicitParameterSyntaxHandling)) { return; } @@ -109,7 +104,7 @@ public override void VisitParameter(ParameterSyntax node) AddCecilExpressions(Context, exps); } - HandleAttributesInMemberDeclaration(node.AttributeLists, paramVar, VariableMemberKind.Parameter); + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, paramVar, VariableMemberKind.Parameter); base.VisitParameter(node); } @@ -136,16 +131,15 @@ private void ProcessMethodDeclarationInternal( var methodVar = AddOrUpdateMethodDefinition( methodSymbol, - declaringTypeName, variableName, simpleName, methodName, - modifiersTokens.MethodModifiersToCecil(GetSpecificModifiers(), methodSymbol), + modifiersTokens.MethodModifiersToCecil(Context, GetSpecificModifiers(), methodSymbol), parameters, typeParameters); - HandleAttributesInMemberDeclaration(attributes, TargetDoesNotMatch, SyntaxKind.ReturnKeyword, methodVar, VariableMemberKind.None); // Normal method attrs. - HandleAttributesInMemberDeclaration(attributes, TargetMatches, SyntaxKind.ReturnKeyword, $"{methodVar}.MethodReturnType", VariableMemberKind.None); // [return:Attr] + HandleAttributesInMemberDeclaration(simpleName, attributes, TargetDoesNotMatch, SyntaxKind.ReturnKeyword, methodVar, VariableMemberKind.None); // Normal method attrs. + HandleAttributesInMemberDeclaration(simpleName, attributes, TargetMatches, SyntaxKind.ReturnKeyword, $"{methodVar}.MethodReturnType", VariableMemberKind.None); // [return:Attr] AddToOverridenMethodsIfAppropriated(methodVar, methodSymbol); @@ -160,7 +154,13 @@ private void ProcessMethodDeclarationInternal( // the latter is a `mangled name` and any reference to the method will use its `unmangled name` for lookups which would fail // should we use `methodName` as the registered name. var nameUsedInRegisteredVariable = methodSymbol.MethodKind == MethodKind.LocalFunction ? simpleName : methodName; - WithCurrentMethod(declaringTypeName, methodVar, nameUsedInRegisteredVariable, parameters.Select(p => Context.SemanticModel.GetDeclaredSymbol(p).Type.ToDisplayString()).ToArray(), methodSymbol.TypeParameters.Length, runWithCurrent); + WithCurrentMethod( + declaringTypeName, + methodVar, + nameUsedInRegisteredVariable, + parameters.Select(p => Context.SemanticModel.GetDeclaredSymbol(p).Type.ToDisplayString()).ToArray(), + methodSymbol.TypeParameters.Select(tp => tp.Name).ToArray(), + runWithCurrent); if (!methodSymbol.IsAbstract && !node.DescendantNodes().Any(n => n.IsKind(SyntaxKind.ReturnStatement))) { @@ -169,12 +169,17 @@ private void ProcessMethodDeclarationInternal( } else { - Context.DefinitionVariables.RegisterMethod(declaringTypeName, methodName, parameters.Select(p => Context.GetTypeInfo(p.Type).Type.ToDisplayString()).ToArray(), typeParameters.Count, methodVar); + Context.DefinitionVariables.RegisterMethod( + declaringTypeName, + methodName, + parameters.Select(p => Context.GetTypeInfo(p.Type).Type.ToDisplayString()).ToArray(), + typeParameters.Select(tp => tp.Identifier.Text).ToArray(), + methodVar); } } } - private string AddOrUpdateMethodDefinition(IMethodSymbol methodSymbol, string declaringTypeName, string variableName, string simpleName, string methodName, string methodModifiers, SeparatedSyntaxList parameters, IList typeParameters) + private string AddOrUpdateMethodDefinition(IMethodSymbol methodSymbol, string variableName, string simpleName, string methodName, string methodModifiers, SeparatedSyntaxList parameters, IList typeParameters) { var tbf = methodSymbol.AsMethodDefinitionVariable(); var found = Context.DefinitionVariables.GetMethodVariable(tbf); @@ -189,8 +194,7 @@ private string AddOrUpdateMethodDefinition(IMethodSymbol methodSymbol, string de // inside AddMethodDefinition() call bellow. if (ilVar == null && !methodSymbol.IsExtern) { - var ilContext = Context.ApiDriver.NewIlContext(Context, simpleName, found.VariableName); - ilVar = ilContext.VariableName; + ilVar = Context.ApiDriver.NewIlContext(Context, simpleName, found.VariableName); } var declaringTypeVarName = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Type).VariableName; @@ -202,7 +206,7 @@ private string AddOrUpdateMethodDefinition(IMethodSymbol methodSymbol, string de return variableName; } - protected void ProcessMethodDeclaration(T node, string variableName, string simpleName, string fqName, bool refReturn, Action runWithCurrent, IList typeParameters = null) where T : BaseMethodDeclarationSyntax + protected void ProcessMethodDeclaration(T node, string variableName, string simpleName, string fqName, Action runWithCurrent, IList typeParameters = null) where T : BaseMethodDeclarationSyntax { var methodSymbol = Context.GetDeclaredSymbol(node); ProcessMethodDeclarationInternal( @@ -232,19 +236,12 @@ private void AddMethodDefinition( context.WriteComment($"Method : {methodName}"); TypeDeclarationVisitor.EnsureForwardedTypeDefinition(context, methodSymbol.ReturnType, []); - var ilContext = context.ApiDriver.NewIlContext(context, simpleName, methodVar); + ilVar = methodSymbol.IsExtern ? new EmptyBodyIlContext(methodVar) : context.ApiDriver.NewIlContext(context, simpleName, methodVar); var declaringTypeVarName = context.DefinitionVariables.GetLastOf(VariableMemberKind.Type).VariableName; - var parameterSymbols = parameters.Select(p => context.SemanticModel.GetDeclaredSymbol(p)).ToArray(); - var exps = context.ApiDefinitionsFactory.Method(context, methodSymbol, new BodiedMemberDefinitionContext(methodName, simpleName, methodVar, declaringTypeVarName, MemberOptions.None, ilContext), methodName, methodModifiers, typeParameters); + var exps = context.ApiDefinitionsFactory.Method(context, methodSymbol, new BodiedMemberDefinitionContext(methodName, simpleName, methodVar, declaringTypeVarName, MemberOptions.None, ilVar), methodName, methodModifiers, typeParameters); AddCecilExpressions(context, exps); - //TODO: Temporary setting ilVar until we change its type to IlContext... - if (!methodSymbol.IsAbstract && methodSymbol.ContainingType.TypeKind != TypeKind.Interface && !methodSymbol.IsExtern) - { - ilVar = ilContext.VariableName; - } - HandleAttributesInTypeParameter(context, typeParameters); } diff --git a/Cecilifier.Core/AST/NoArgsValueTypeObjectCreatingInAssignmentVisitor.cs b/Cecilifier.Core/AST/NoArgsValueTypeObjectCreatingInAssignmentVisitor.cs index f5621005..b4ace754 100644 --- a/Cecilifier.Core/AST/NoArgsValueTypeObjectCreatingInAssignmentVisitor.cs +++ b/Cecilifier.Core/AST/NoArgsValueTypeObjectCreatingInAssignmentVisitor.cs @@ -10,12 +10,12 @@ namespace Cecilifier.Core.AST { internal class NoArgsValueTypeObjectCreatingInAssignmentVisitor : SyntaxWalkerBase { - private readonly string ilVar; + private readonly IlContext ilVar; private readonly ResolvedType instantiatedType; private readonly Func tempValueTypeDeclarer; private readonly BaseObjectCreationExpressionSyntax objectCreationExpression; - internal NoArgsValueTypeObjectCreatingInAssignmentVisitor(IVisitorContext ctx, string ilVar, ResolvedType instantiatedType, Func tempValueTypeDeclarer, + internal NoArgsValueTypeObjectCreatingInAssignmentVisitor(IVisitorContext ctx, IlContext ilVar, ResolvedType instantiatedType, Func tempValueTypeDeclarer, BaseObjectCreationExpressionSyntax objectCreationExpression) : base(ctx) { this.ilVar = ilVar; @@ -31,8 +31,8 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax if (InlineArrayProcessor.TryHandleIntIndexElementAccess(Context, ilVar, node, out var elementType)) { var tempVar = tempValueTypeDeclarer(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, tempVar.VariableName); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Stobj, Context.TypeResolver.ResolveAny(elementType, ResolveTargetKind.Instruction)); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, tempVar.VariableName.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Stobj, Context.TypeResolver.Resolve(elementType, ResolveTargetKind.Instruction).Expression.AsToken()); return; } diff --git a/Cecilifier.Core/AST/NonCapturingLambdaProcessor.cs b/Cecilifier.Core/AST/NonCapturingLambdaProcessor.cs index 50ddd18b..db0a8c90 100644 --- a/Cecilifier.Core/AST/NonCapturingLambdaProcessor.cs +++ b/Cecilifier.Core/AST/NonCapturingLambdaProcessor.cs @@ -78,7 +78,7 @@ private static void InjectSyntheticMethodsFor(IVisitorContext context, string de "MethodAttributes.Public | MethodAttributes.Static", lambda.ParameterList().Select(ParamSpecFor).ToArray(), [], - ctx => ctx.TypeResolver.ResolveAny(returnType, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(returnType, ResolveTargetKind.ReturnType), out var methodDefinitionVariable); context.Generate(methodExps); @@ -87,14 +87,13 @@ ParameterSpec ParamSpecFor(ParameterSyntax parameter, int parameterIndex) { return new ParameterSpec( parameter.Identifier.Text, - context.TypeResolver.ResolveAny(namedTypeSymbol.TypeArguments[parameterIndex], ResolveTargetKind.Parameter), + context.TypeResolver.Resolve(namedTypeSymbol.TypeArguments[parameterIndex], ResolveTargetKind.Parameter), RefKind.None, parameter.Default != null ? Constants.ParameterAttributes.Optional : Constants.ParameterAttributes.None, parameter.Accept(DefaultParameterExtractorVisitor.Instance)); } - var syntheticIlVar = context.Naming.ILProcessor(syntheticMethodName); - context.Generate($"var {syntheticIlVar} = {methodVar}.Body.GetILProcessor();"); + var syntheticIlVar = context.ApiDriver.NewIlContext(context, syntheticMethodName, methodVar); context.WriteNewLine(); using (context.DefinitionVariables.WithVariable(methodDefinitionVariable)) diff --git a/Cecilifier.Core/AST/NullLiteralArgumentDecorator.cs b/Cecilifier.Core/AST/NullLiteralArgumentDecorator.cs index d17a878f..2212c705 100644 --- a/Cecilifier.Core/AST/NullLiteralArgumentDecorator.cs +++ b/Cecilifier.Core/AST/NullLiteralArgumentDecorator.cs @@ -25,9 +25,9 @@ internal ref struct NullLiteralArgumentDecorator { private string _localVariableName; private readonly IVisitorContext _context; - private readonly string _ilVar; + private readonly IlContext _ilVar; - public NullLiteralArgumentDecorator(IVisitorContext context, ArgumentSyntax node, string ilVar) + public NullLiteralArgumentDecorator(IVisitorContext context, ArgumentSyntax node, IlContext ilVar) { if (node.Expression is not LiteralExpressionSyntax { RawKind: (int) SyntaxKind.NullLiteralExpression }) return; @@ -38,7 +38,7 @@ public NullLiteralArgumentDecorator(IVisitorContext context, ArgumentSyntax node // we have a `null` being passed to a Nullable parameter so we need to emit code // for steps 1 & 2 as outlined in the remarks section above. - var local = context.AddLocalVariableToCurrentMethod("tmpNull", context.TypeResolver.ResolveAny(argType, ResolveTargetKind.LocalVariable)); + var local = context.AddLocalVariableToCurrentMethod("tmpNull", context.TypeResolver.Resolve(argType, ResolveTargetKind.LocalVariable)); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldloca_S, local.VariableName); _localVariableName = local.VariableName; diff --git a/Cecilifier.Core/AST/Params/ArrayExpandedParamsArgumentHandler.cs b/Cecilifier.Core/AST/Params/ArrayExpandedParamsArgumentHandler.cs index b9298128..08634d39 100644 --- a/Cecilifier.Core/AST/Params/ArrayExpandedParamsArgumentHandler.cs +++ b/Cecilifier.Core/AST/Params/ArrayExpandedParamsArgumentHandler.cs @@ -12,14 +12,14 @@ namespace Cecilifier.Core.AST.Params; internal class ArrayExpandedParamsArgumentHandler : ExpandedParamsArgumentHandler { - public ArrayExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, string ilVar) : base(context, paramsParameter, argumentList, ilVar) + public ArrayExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, IlContext ilVar) : base(context, paramsParameter, argumentList, ilVar) { _currentIndex = 0; _stelemOpCode = ElementType.StelemOpCode(); - _backingVariableName = Context.AddLocalVariableToCurrentMethod($"{paramsParameter.Name}Params", Context.TypeResolver.ResolveAny(paramsParameter.Type, ResolveTargetKind.LocalVariable)); + _backingVariableName = Context.AddLocalVariableToCurrentMethod($"{paramsParameter.Name}Params", Context.TypeResolver.Resolve(paramsParameter.Type, ResolveTargetKind.LocalVariable)); - var paramsType = Context.TypeResolver.ResolveAny(ElementType,ResolveTargetKind.Instruction); + var paramsType = Context.TypeResolver.Resolve(ElementType,ResolveTargetKind.Instruction); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldc_I4, ElementCount); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Newarr, paramsType.AsToken()); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Stloc, new CilLocalVariableHandle(_backingVariableName)); diff --git a/Cecilifier.Core/AST/Params/ExpandedParamsArgumentHandler.cs b/Cecilifier.Core/AST/Params/ExpandedParamsArgumentHandler.cs index 6f3fef08..7d29e58a 100644 --- a/Cecilifier.Core/AST/Params/ExpandedParamsArgumentHandler.cs +++ b/Cecilifier.Core/AST/Params/ExpandedParamsArgumentHandler.cs @@ -25,10 +25,10 @@ namespace Cecilifier.Core.AST.Params; /// internal abstract class ExpandedParamsArgumentHandler { - protected readonly string ilVar; + protected readonly IlContext ilVar; protected int _currentIndex; - protected ExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, string ilVar) + protected ExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, IlContext ilVar) { Context = context; ElementType = paramsParameter.Type.ElementTypeSymbolOf(); diff --git a/Cecilifier.Core/AST/Params/ReadOnlySpanExpandedParamsArgumentHandler.cs b/Cecilifier.Core/AST/Params/ReadOnlySpanExpandedParamsArgumentHandler.cs index 5d0a765b..eef9eeb5 100644 --- a/Cecilifier.Core/AST/Params/ReadOnlySpanExpandedParamsArgumentHandler.cs +++ b/Cecilifier.Core/AST/Params/ReadOnlySpanExpandedParamsArgumentHandler.cs @@ -8,7 +8,7 @@ namespace Cecilifier.Core.AST.Params; internal class ReadOnlySpanExpandedParamsArgumentHandler : SpanExpandedParamsArgumentHandler { - public ReadOnlySpanExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, string ilVar) + public ReadOnlySpanExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, IlContext ilVar) : base(context, paramsParameter, argumentList, ilVar) { } diff --git a/Cecilifier.Core/AST/Params/SpanExpandedParamsArgumentHandler.cs b/Cecilifier.Core/AST/Params/SpanExpandedParamsArgumentHandler.cs index 964e23ef..a7ac4afd 100644 --- a/Cecilifier.Core/AST/Params/SpanExpandedParamsArgumentHandler.cs +++ b/Cecilifier.Core/AST/Params/SpanExpandedParamsArgumentHandler.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Reflection.Emit; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -17,13 +18,13 @@ internal class SpanExpandedParamsArgumentHandler : ExpandedParamsArgumentHandler private readonly ResolvedType _inlineArrayType; private readonly ITypeSymbol _paramsParameterType; - public SpanExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, string ilVar) : base(context, paramsParameter, argumentList, ilVar) + public SpanExpandedParamsArgumentHandler(IVisitorContext context, IParameterSymbol paramsParameter, ArgumentListSyntax argumentList, IlContext ilVar) : base(context, paramsParameter, argumentList, ilVar) { _paramsParameterType = paramsParameter.Type.ElementTypeSymbolOf(); _stindOpCode = _paramsParameterType.StindOpCodeFor(); var openInlineArrayType = InlineArrayGenerator.GetOrGenerateInlineArrayType(context, argumentList.Arguments.Count, "InlineArray to store the `params` values."); - _inlineArrayType = openInlineArrayType.MakeGenericInstanceType([context.TypeResolver.ResolveAny(_paramsParameterType, ResolveTargetKind.None)]); + _inlineArrayType = openInlineArrayType.MakeGenericInstanceType([context.TypeResolver.Resolve(_paramsParameterType, ResolveTargetKind.None)]); var inlineArrayBuffer = context.AddLocalVariableToCurrentMethod($"{paramsParameter.Name}Arg", _inlineArrayType); _inlineArrayVariableName = inlineArrayBuffer.VariableName; @@ -59,6 +60,7 @@ public override void PostProcessArgumentList(ArgumentListSyntax argumentList) string MakeGenericInstanceMethod(DefinitionVariable genericMethodVariable) { - return genericMethodVariable.VariableName.MakeGenericInstanceMethod(Context, genericMethodVariable.MemberName, [ _inlineArrayType, Context.TypeResolver.ResolveAny(_paramsParameterType, ResolveTargetKind.TypeReference)]); + IReadOnlyList resolvedTypeArguments = [ _inlineArrayType, Context.TypeResolver.Resolve(_paramsParameterType, ResolveTargetKind.TypeReference)]; + return Context.MemberResolver.MakeGeneticInstanceMethod(genericMethodVariable.VariableName, genericMethodVariable.MemberName, resolvedTypeArguments); } } diff --git a/Cecilifier.Core/AST/PropertyDeclarationVisitor.cs b/Cecilifier.Core/AST/PropertyDeclarationVisitor.cs index 39334eea..da871a49 100644 --- a/Cecilifier.Core/AST/PropertyDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/PropertyDeclarationVisitor.cs @@ -15,7 +15,6 @@ using Cecilifier.Core.Naming; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; -using FieldAttributes = Mono.Cecil.FieldAttributes; #nullable enable namespace Cecilifier.Core.AST @@ -60,8 +59,8 @@ public override void VisitIndexerDeclaration(IndexerDeclarationSyntax node) var propDefVar = AddPropertyDefinition(node, propertyDeclaringTypeVar.VariableName, propertyDeclaringTypeVar.MemberName, propName, propertyParameters, propertyType); var backingFieldVar = ProcessPropertyAccessors(node, propertyDeclaringTypeVar.VariableName, propDefVar, propName, propertyParameters, node.ExpressionBody); - HandleAttributesInMemberDeclaration(node.AttributeLists, TargetDoesNotMatch, SyntaxKind.FieldKeyword, propDefVar, VariableMemberKind.None); // Normal property attrs - HandleAttributesInMemberDeclaration(node.AttributeLists, TargetMatches, SyntaxKind.FieldKeyword, backingFieldVar ?? String.Empty, VariableMemberKind.Field); // [field: attr], i.e, attr belongs to the backing field. + HandleAttributesInMemberDeclaration(propName, node.AttributeLists, TargetDoesNotMatch, SyntaxKind.FieldKeyword, propDefVar, VariableMemberKind.None); // Normal property attrs + HandleAttributesInMemberDeclaration(propName, node.AttributeLists, TargetMatches, SyntaxKind.FieldKeyword, backingFieldVar ?? String.Empty, VariableMemberKind.Field); // [field: attr], i.e, attr belongs to the backing field. } public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) @@ -79,10 +78,10 @@ public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) var propDefVar = AddPropertyDefinition(node, propertyDeclaringTypeVar.VariableName, propertyDeclaringTypeVar.MemberName, propName, [], propertyType); var backingFieldVar = ProcessPropertyAccessors(node, propertyDeclaringTypeVar.VariableName, propDefVar, node.Identifier.ValueText, NoParameters, node.ExpressionBody); - HandleAttributesInMemberDeclaration(node.AttributeLists, TargetDoesNotMatch, SyntaxKind.FieldKeyword, propDefVar, VariableMemberKind.None); // Normal property attrs + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, TargetDoesNotMatch, SyntaxKind.FieldKeyword, propDefVar, VariableMemberKind.None); // Normal property attrs // Attributes targeting backing field is only valid on auto-properties. if (node.AccessorList?.Accessors.All(a => a.Body == null && a.ExpressionBody == null) == true) - HandleAttributesInMemberDeclaration(node.AttributeLists, TargetMatches, SyntaxKind.FieldKeyword, backingFieldVar ?? string.Empty, VariableMemberKind.Field); // [field: attr], i.e, attr belongs to the backing field. + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, TargetMatches, SyntaxKind.FieldKeyword, backingFieldVar ?? string.Empty, VariableMemberKind.Field); // [field: attr], i.e, attr belongs to the backing field. } private bool PropertyAlreadyProcessed(BasePropertyDeclarationSyntax node) @@ -129,7 +128,6 @@ bool AttributeAlreadyAddedForAnotherMember() var propertyGenerationData = new PropertyGenerationData( propertySymbol.ContainingType.ToDisplayString(), propertyDeclaringTypeVar, - // is INamedTypeSymbol { IsGenericType: true} && propertySymbol.IsDefinedInCurrentAssembly(Context) (INamedTypeSymbol) propertySymbol.ContainingSymbol, propDefVar, propertyName, @@ -193,11 +191,11 @@ void AddSetterMethod(IPropertySymbol property, AccessorDeclarationSyntax accesso } else if (accessor.Body != null) { - StatementVisitor.Visit(Context, ilContext, accessor.Body); + StatementVisitor.Visit(Context, ilContext!, accessor.Body); } else { - ExpressionVisitor.Visit(Context, ilContext, accessor.ExpressionBody!); + ExpressionVisitor.Visit(Context, ilContext!, accessor.ExpressionBody!); } Context.ApiDriver.WriteCilInstruction(Context, ilContext!, OpCodes.Ret); @@ -248,7 +246,7 @@ void AddGetterMethod(AccessorDeclarationSyntax accessor) } } - void ProcessExpressionBodiedGetter(string ilVar, ArrowExpressionClauseSyntax? expression) + void ProcessExpressionBodiedGetter(IlContext ilVar, ArrowExpressionClauseSyntax? expression) { ExpressionVisitor.Visit(Context, ilVar, expression!); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ret); @@ -260,7 +258,7 @@ void ProcessExpressionBodiedGetter(string ilVar, ArrowExpressionClauseSyntax? ex if (node.AccessorList == null) { Debug.Assert(propertySymbol.GetMethod != null); - var accessorModifiers = node.Modifiers.MethodModifiersToCecil(Constants.Cecil.MethodAttributesSpecialName, propertySymbol.GetMethod); + var accessorModifiers = node.Modifiers.MethodModifiersToCecil(Context,Constants.Cecil.MethodAttributesSpecialName, propertySymbol.GetMethod); return new Dictionary() { ["get"] = accessorModifiers @@ -284,7 +282,7 @@ void ProcessExpressionBodiedGetter(string ilVar, ArrowExpressionClauseSyntax? ex : node.Modifiers; var accessorSymbol = accessorKind == SyntaxKind.GetAccessorDeclaration ? propertySymbol.GetMethod : propertySymbol.SetMethod; - return modifiers.MethodModifiersToCecil(Constants.Cecil.MethodAttributesSpecialName, accessorSymbol); + return modifiers.MethodModifiersToCecil(Context, Constants.Cecil.MethodAttributesSpecialName, accessorSymbol); } } @@ -297,7 +295,7 @@ private static string BackingFieldModifiersFor(BasePropertyDeclarationSyntax nod if (node.AccessorList != null && node.AccessorList.Accessors.Any(acc => acc.IsKind(SyntaxKind.InitAccessorDeclaration))) m = m.Append(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword)); // properties with `init` accessors are considered `readonly` - return ModifiersToCecil(m, "Private", FieldDeclarationVisitor.MapFieldAttributesFor); + return ModifiersAsString(m, "Private", FieldDeclarationVisitor.MapFieldAttributesFor); } private string AddPropertyDefinition(BasePropertyDeclarationSyntax propertyDeclarationSyntax, string declaringTypeVariable, string declaringTypeName, string propName, List propertyParameters, ResolvedType propertyType) diff --git a/Cecilifier.Core/AST/StatementVisitor.ForEach.cs b/Cecilifier.Core/AST/StatementVisitor.ForEach.cs index 9927ac77..55086e01 100644 --- a/Cecilifier.Core/AST/StatementVisitor.ForEach.cs +++ b/Cecilifier.Core/AST/StatementVisitor.ForEach.cs @@ -28,14 +28,20 @@ void ProcessForEachOverArray() // save array in local variable... var arrayVariable = CodeGenerationHelpers.StoreTopOfStackInLocalVariable(Context, _ilVar, "array", enumerableType); - var loopVariable = Context.AddLocalVariableToCurrentMethod(node.Identifier.ValueText, Context.TypeResolver.ResolveAny(enumerableType.ElementTypeSymbolOf(), ResolveTargetKind.LocalVariable)).VariableName; - var loopIndexVar = Context.AddLocalVariableToCurrentMethod("index", Context.TypeResolver.ResolveAny(Context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.LocalVariable)).VariableName; - - var conditionCheckLabelVar = CreateCilInstruction(_ilVar, OpCodes.Nop); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Br, conditionCheckLabelVar); - var firstLoopBodyInstructionVar = CreateCilInstruction(_ilVar, OpCodes.Ldloc, arrayVariable.VariableName); - WriteCecilExpression(Context, $"{_ilVar}.Append({firstLoopBodyInstructionVar});"); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, loopIndexVar); + var conditionCheckLabelVar = Context.Naming.Label("ConditionCheckLabel"); + var firstLoopBodyInstructionLabelVar = Context.Naming.Label("FirstLoopBodyInstructionLabel"); + + Context.ApiDriver.DefineLabel(Context, _ilVar, conditionCheckLabelVar); + Context.ApiDriver.DefineLabel(Context, _ilVar, firstLoopBodyInstructionLabelVar); + + var loopVariable = Context.AddLocalVariableToCurrentMethod(node.Identifier.ValueText, Context.TypeResolver.Resolve(enumerableType.ElementTypeSymbolOf(), ResolveTargetKind.LocalVariable)).VariableName; + var loopIndexVar = Context.AddLocalVariableToCurrentMethod("index", Context.TypeResolver.Resolve(Context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.LocalVariable)).VariableName; + + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Br, conditionCheckLabelVar); + + Context.ApiDriver.MarkLabel(Context, _ilVar, firstLoopBodyInstructionLabelVar); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(arrayVariable.VariableName)); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(loopIndexVar)); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, enumerableType.ElementTypeSymbolOf().LdelemOpCode()); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Stloc, new CilLocalVariableHandle(loopVariable)); @@ -48,12 +54,12 @@ void ProcessForEachOverArray() Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Stloc, new CilLocalVariableHandle(loopIndexVar)); // condition check... - WriteCecilExpression(Context, $"{_ilVar}.Append({conditionCheckLabelVar});"); + Context.ApiDriver.MarkLabel(Context, _ilVar, conditionCheckLabelVar); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(loopIndexVar)); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, arrayVariable.VariableName); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(arrayVariable.VariableName)); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldlen); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Conv_I4); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Blt, firstLoopBodyInstructionVar); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Blt, firstLoopBodyInstructionLabelVar); } void ProcessForEachOverEnumerable() @@ -95,7 +101,7 @@ private void ProcessForEachFinally(ForEachHandlerContext forEachHandlerContext) if (forEachHandlerContext.GetEnumeratorMethod.ReturnType.IsValueType || forEachHandlerContext.GetEnumeratorMethod.ReturnType.TypeKind == TypeKind.TypeParameter) { Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, forEachHandlerContext.EnumeratorVariableName); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Constrained, Context.TypeResolver.ResolveAny(forEachHandlerContext.GetEnumeratorMethod.ReturnType, ResolveTargetKind.Instruction)); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Constrained, Context.TypeResolver.Resolve(forEachHandlerContext.GetEnumeratorMethod.ReturnType, ResolveTargetKind.Instruction)); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Callvirt, Context.RoslynTypeSystem.SystemIDisposable.GetMembers("Dispose").OfType().Single().MethodResolverExpression(Context)); } else @@ -105,7 +111,8 @@ private void ProcessForEachFinally(ForEachHandlerContext forEachHandlerContext) Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Brfalse_S, skipDisposeMethodCallNopVar); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, forEachHandlerContext.EnumeratorVariableName); Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Callvirt, Context.RoslynTypeSystem.SystemIDisposable.GetMembers("Dispose").OfType().Single().MethodResolverExpression(Context)); - AddCecilExpression($"{_ilVar}.Append({skipDisposeMethodCallNopVar});"); + //TODO: Mono.Cecil specific + AddCecilExpression($"{_ilVar.VariableName}.Append({skipDisposeMethodCallNopVar});"); } } @@ -114,7 +121,7 @@ private void ProcessForEach(ForEachHandlerContext forEachHandlerContext, ForEach // Adds a variable to store current value in the foreach loop. Context.WriteNewLine(); Context.WriteComment("variable to store current value in the foreach loop."); - var resolvedType = Context.TypeResolver.ResolveAny(forEachHandlerContext.EnumeratorCurrentProperty.GetMemberType(), ResolveTargetKind.LocalVariable); + var resolvedType = Context.TypeResolver.Resolve(forEachHandlerContext.EnumeratorCurrentProperty.GetMemberType(), ResolveTargetKind.LocalVariable); var foreachCurrentValueVarName = Context.AddLocalVariableToCurrentMethod(node.Identifier.ValueText, resolvedType).VariableName; var endOfLoopLabelVar = Context.Naming.Label("endForEach"); @@ -148,7 +155,8 @@ private void ProcessForEach(ForEachHandlerContext forEachHandlerContext, ForEach Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Br, forEachLoopBegin); Context.WriteNewLine(); Context.WriteComment("end of foreach loop"); - Context.Generate($"{_ilVar}.Append({endOfLoopLabelVar});"); + //TODO: Mono.Cecil specific + Context.Generate($"{_ilVar.VariableName}.Append({endOfLoopLabelVar});"); Context.WriteNewLine(); } diff --git a/Cecilifier.Core/AST/StatementVisitor.TryCatchFinally.cs b/Cecilifier.Core/AST/StatementVisitor.TryCatchFinally.cs index 7cdda821..21327b21 100644 --- a/Cecilifier.Core/AST/StatementVisitor.TryCatchFinally.cs +++ b/Cecilifier.Core/AST/StatementVisitor.TryCatchFinally.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Reflection.Emit; +using Cecilifier.Core.ApiDriver; +using Cecilifier.Core.Extensions; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; using Microsoft.CodeAnalysis.CSharp; @@ -11,17 +13,18 @@ namespace Cecilifier.Core.AST { internal partial class StatementVisitor { - private void ProcessTryCatchFinallyBlock(string ilVar, CSharpSyntaxNode tryStatement, CatchClauseSyntax[] catches, Action? finallyBlockHandler, TState state = default) + private void ProcessTryCatchFinallyBlock(IlContext ilVar, CSharpSyntaxNode tryStatement, CatchClauseSyntax[] catches, Action? finallyBlockHandler, TState state = default) { ProcessWithInTryCatchFinallyBlock(ilVar, _ => tryStatement.Accept(this), catches, finallyBlockHandler, state); } - private void ProcessWithInTryCatchFinallyBlock(string ilVar, Action toProcess, CatchClauseSyntax[] catches, Action finallyBlockHandler, TState state) + private void ProcessWithInTryCatchFinallyBlock(IlContext ilVar, Action toProcess, CatchClauseSyntax[] catches, Action? finallyBlockHandler, TState state) { var exceptionHandlerTable = new ExceptionHandlerEntry[catches.Length + (finallyBlockHandler != null ? 1 : 0)]; Context.WriteNewLine(); Context.WriteComment("Try start"); + var tryStartVar = AddCilInstructionWithLocalVariable(ilVar, OpCodes.Nop); exceptionHandlerTable[0].TryStart = tryStartVar; @@ -29,11 +32,13 @@ private void ProcessWithInTryCatchFinallyBlock(string ilVar, Action(string ilVar, Action exceptionHandlerTable) + private void HandleCatchClause(IlContext ilVar, CatchClauseSyntax node, ExceptionHandlerEntry[] exceptionHandlerTable, int currentIndex, string firstInstructionAfterTryCatchBlock) { - string methodVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); - foreach (var handlerEntry in exceptionHandlerTable) - { - AddCecilExpression($"{methodVar}.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.{handlerEntry.Kind})"); - AddCecilExpression("{"); - if (handlerEntry.Kind == Mono.Cecil.Cil.ExceptionHandlerType.Catch) - { - AddCecilExpression($" CatchType = {handlerEntry.CatchType},"); - } - - AddCecilExpression($" TryStart = {handlerEntry.TryStart},"); - AddCecilExpression($" TryEnd = {handlerEntry.TryEnd},"); - AddCecilExpression($" HandlerStart = {handlerEntry.HandlerStart},"); - AddCecilExpression($" HandlerEnd = {handlerEntry.HandlerEnd}"); - AddCecilExpression("});"); - } - } - - private void HandleCatchClause(string ilVar, CatchClauseSyntax node, ExceptionHandlerEntry[] exceptionHandlerTable, int currentIndex, string firstInstructionAfterTryCatchBlock) - { - exceptionHandlerTable[currentIndex].Kind = Mono.Cecil.Cil.ExceptionHandlerType.Catch; + exceptionHandlerTable[currentIndex].Kind = ExceptionHandlerKind.Catch; exceptionHandlerTable[currentIndex].HandlerStart = AddCilInstructionWithLocalVariable(ilVar, OpCodes.Pop); // pops the exception object from stack... if (currentIndex == 0) @@ -84,13 +69,13 @@ private void HandleCatchClause(string ilVar, CatchClauseSyntax node, ExceptionHa exceptionHandlerTable[currentIndex].TryStart = exceptionHandlerTable[0].TryStart; exceptionHandlerTable[currentIndex].TryEnd = exceptionHandlerTable[0].TryEnd; - exceptionHandlerTable[currentIndex].CatchType = ResolveType(node.Declaration!.Type, ResolveTargetKind.None); + exceptionHandlerTable[currentIndex].CatchType = ResolveType(node.Declaration!.Type, ResolveTargetKind.TypeReference); VisitCatchClause(node); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Leave, firstInstructionAfterTryCatchBlock); + Context.ApiDriver.WriteCilBranch(Context, ilVar, OpCodes.Leave, firstInstructionAfterTryCatchBlock); } - private void HandleFinallyClause(string ilVar, Action finallyBlockHandler, ExceptionHandlerEntry[] exceptionHandlerTable, TState state) + private void HandleFinallyClause(IlContext ilVar, Action? finallyBlockHandler, ExceptionHandlerEntry[] exceptionHandlerTable, TState state) { if (finallyBlockHandler == null) return; @@ -99,7 +84,7 @@ private void HandleFinallyClause(string ilVar, Action finallyBlo exceptionHandlerTable[finallyEntryIndex].TryStart = exceptionHandlerTable[0].TryStart; exceptionHandlerTable[finallyEntryIndex].TryEnd = exceptionHandlerTable[0].TryEnd; - exceptionHandlerTable[finallyEntryIndex].Kind = Mono.Cecil.Cil.ExceptionHandlerType.Finally; + exceptionHandlerTable[finallyEntryIndex].Kind = ExceptionHandlerKind.Finally; Context.WriteNewLine(); Context.WriteComment("finally start"); @@ -119,15 +104,5 @@ private void HandleFinallyClause(string ilVar, Action finallyBlo Context.WriteNewLine(); Context.WriteComment("finally end"); } - - private struct ExceptionHandlerEntry - { - public Mono.Cecil.Cil.ExceptionHandlerType Kind; - public ResolvedType CatchType; - public string TryStart; - public string TryEnd; - public string HandlerStart; - public string HandlerEnd; - } } } diff --git a/Cecilifier.Core/AST/StatementVisitor.cs b/Cecilifier.Core/AST/StatementVisitor.cs index ebcc4640..7c8c8107 100644 --- a/Cecilifier.Core/AST/StatementVisitor.cs +++ b/Cecilifier.Core/AST/StatementVisitor.cs @@ -20,14 +20,15 @@ namespace Cecilifier.Core.AST { internal partial class StatementVisitor : SyntaxWalkerBase { - private string _ilVar; + private IlContext _ilVar; - private StatementVisitor(IVisitorContext ctx, string ilVar) : base(ctx) + private StatementVisitor(IVisitorContext ctx, IlContext ilVar) : base(ctx) { _ilVar = ilVar; + _ilVar.Materialize(); } - internal static void Visit(IVisitorContext context, string ilVar, CSharpSyntaxNode node) + internal static void Visit(IVisitorContext context, IlContext ilVar, CSharpSyntaxNode node) { node.Accept(new StatementVisitor(context, ilVar)); } @@ -88,14 +89,16 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Stloc, new CilLocalVariableHandle(evaluatedExpressionVariable)); // stores evaluated expression in local var // Add label to end of switch - var endOfSwitchLabel = CreateCilInstruction(_ilVar, Context.Naming.Label("endOfSwitch"), OpCodes.Nop); + var endOfSwitchLabel = Context.Naming.Label("endOfSwitch"); + Context.ApiDriver.DefineLabel(Context, _ilVar, endOfSwitchLabel); + breakToInstructionVars.Push(endOfSwitchLabel); // Write the switch conditions. var nextTestLabels = node.Sections.Select( (_, index) => { var labelName = Context.Naming.Label($"caseCode_{index}"); - CreateCilInstruction(_ilVar, labelName, OpCodes.Nop); + Context.ApiDriver.DefineLabel(Context, _ilVar, labelName); return labelName; }).ToArray(); @@ -105,7 +108,7 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) { if (switchSection.Labels.First().Kind() == SyntaxKind.DefaultSwitchLabel) { - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Br, nextTestLabels[currentLabelIndex]); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Br, nextTestLabels[currentLabelIndex]); hasDefault = true; continue; } @@ -116,9 +119,9 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) Context.WriteNewLine(); Context.WriteComment($"{sectionLabel.ToString()} (condition)"); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, evaluatedExpressionVariable); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, evaluatedExpressionVariable.AsLocalVariable()); ExpressionVisitor.Visit(Context, _ilVar, sectionLabel); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Beq_S, nextTestLabels[currentLabelIndex]); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Beq_S, nextTestLabels[currentLabelIndex]); } currentLabelIndex++; } @@ -126,7 +129,7 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) // if at runtime the code hits this point and the switch does not have a default section // it means none of the labels matched so just jump to the end of the switch. if (!hasDefault) - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Br, endOfSwitchLabel); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Br, endOfSwitchLabel); // Write the statements for each switch section... currentLabelIndex = 0; @@ -135,7 +138,7 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) using var _ = LineInformationTracker.Track(Context, switchSection); Context.WriteNewLine(); Context.WriteComment($"{switchSection.Labels.First().ToString()} (code)"); - AddCecilExpression($"{_ilVar}.Append({nextTestLabels[currentLabelIndex]});"); + Context.ApiDriver.MarkLabel(Context, _ilVar, nextTestLabels[currentLabelIndex]); foreach (var statement in switchSection.Statements) { statement.Accept(this); @@ -145,7 +148,7 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node) Context.WriteNewLine(); Context.WriteComment("End of switch"); - AddCecilExpression($"{_ilVar}.Append({endOfSwitchLabel});"); + Context.ApiDriver.MarkLabel(Context, _ilVar, endOfSwitchLabel); breakToInstructionVars.Pop(); } @@ -157,7 +160,7 @@ public override void VisitBreakStatement(BreakStatementSyntax node) throw new InvalidOperationException("Invalid break."); } - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Br, breakToInstructionVars.Peek()); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Br, breakToInstructionVars.Peek()); } public override void VisitFixedStatement(FixedStatementSyntax node) @@ -169,7 +172,7 @@ public override void VisitFixedStatement(FixedStatementSyntax node) var currentMethodVar = Context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); var localVar = node.Declaration.Variables[0]; - var resolvedVarType = Context.TypeResolver.ResolveAny(pointerType.PointedAtType, ResolveTargetKind.LocalVariable).MakeByReferenceType(); + var resolvedVarType = Context.TypeResolver.MakeByRefType(Context.TypeResolver.Resolve(pointerType.PointedAtType, new TypeResolutionContext(ResolveTargetKind.LocalVariable, TypeResolutionOptions.IsByRef))); Context.ApiDefinitionsFactory.LocalVariable(Context, localVar.Identifier.Text, currentMethodVar.VariableName, resolvedVarType); ProcessVariableInitialization(localVar, declaredType); } @@ -264,22 +267,24 @@ void FinallyBlockHandler(ForEachHandlerContext _) string? lastFinallyInstructionLabel = null; if (usingType.TypeKind == TypeKind.TypeParameter || usingType.IsValueType) { - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, localVarDef); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Constrained, $"{localVarDef}.VariableType"); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloca, localVarDef.AsLocalVariable()); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Constrained, Context.TypeResolver.Resolve(usingType, ResolveTargetKind.TypeReference).AsToken()); } else { lastFinallyInstructionLabel = Context.Naming.SyntheticVariable("endFinally", ElementKind.Label); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, localVarDef); - CreateCilInstruction(_ilVar, lastFinallyInstructionLabel, OpCodes.Nop); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Brfalse, lastFinallyInstructionLabel, "check if the disposable is not null"); - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, localVarDef); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, localVarDef.AsLocalVariable()); + Context.ApiDriver.DefineLabel(Context, _ilVar, lastFinallyInstructionLabel); + Context.ApiDriver.WriteCilBranch(Context, _ilVar, OpCodes.Brfalse, lastFinallyInstructionLabel, "check if the disposable is not null"); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Ldloc, localVarDef.AsLocalVariable()); } - Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Callvirt, Context.RoslynTypeSystem.SystemIDisposable.GetMembers("Dispose").OfType().Single().MethodResolverExpression(Context)); + Context.ApiDriver.WriteCilInstruction(Context, _ilVar, OpCodes.Callvirt, Context.RoslynTypeSystem.SystemIDisposable.GetMembers("Dispose").OfType().Single().MethodResolverExpression(Context).AsToken()); if (lastFinallyInstructionLabel != null) - AddCecilExpression($"{_ilVar}.Append({lastFinallyInstructionLabel});"); + { + Context.ApiDriver.MarkLabel(Context, _ilVar, lastFinallyInstructionLabel); + } } ProcessTryCatchFinallyBlock(_ilVar, node.Statement, Array.Empty(), FinallyBlockHandler); diff --git a/Cecilifier.Core/AST/SyntaxWalkerBase.cs b/Cecilifier.Core/AST/SyntaxWalkerBase.cs index 4e6ab818..001363f2 100644 --- a/Cecilifier.Core/AST/SyntaxWalkerBase.cs +++ b/Cecilifier.Core/AST/SyntaxWalkerBase.cs @@ -4,19 +4,15 @@ using System.Linq; using System.Reflection.Emit; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.Handles; using Cecilifier.Core.Extensions; using Cecilifier.Core.Misc; -using Cecilifier.Core.Naming; using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Mono.Cecil; using CecilOpCodes = Mono.Cecil.Cil.OpCodes; using static Cecilifier.Core.Misc.CodeGenerationHelpers; @@ -52,44 +48,48 @@ protected void AddCecilExpression(string format, params object[] args) Context.WriteNewLine(); } - protected void AddCilInstruction(string ilVar, OpCode opCode, ITypeSymbol type) + protected void AddCilInstruction(IlContext ilVar, OpCode opCode, ITypeSymbol type) { - var operand = Context.TypeResolver.ResolveAny(type, new TypeResolutionContext(ResolveTargetKind.Instruction, type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); + var operand = Context.TypeResolver.Resolve(type, new TypeResolutionContext(ResolveTargetKind.Instruction, type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode, new CilToken(operand.Expression)); } - protected string AddCilInstructionWithLocalVariable(string ilVar, OpCode opCode) + protected string AddCilInstructionWithLocalVariable(IlContext ilVar, OpCode opCode) { - var instVar = CreateCilInstruction(ilVar, opCode); - AddCecilExpression($"{ilVar}.Append({instVar});"); + var labelVariable = Context.Naming.Label("label"); - return instVar; + Context.ApiDriver.DefineLabel(Context, ilVar, labelVariable); + Context.ApiDriver.MarkLabel(Context, ilVar, labelVariable); + if (opCode != OpCodes.Nop) + Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode); + + return labelVariable; } - protected string CreateCilInstruction(string ilVar, OpCode opCode, object operand = null) + protected string CreateCilInstruction(IlContext ilVar, OpCode opCode, object operand = null) { var operandStr = operand == null ? string.Empty : $", {operand}"; var instVar = Context.Naming.Instruction(opCode.OpCodeName()); - AddCecilExpression($"var {instVar} = {ilVar}.Create({opCode.ConstantName()}{operandStr});"); + AddCecilExpression($"var {instVar} = {ilVar.VariableName}.Create({opCode.ConstantName()}{operandStr});"); return instVar; } - protected string CreateCilInstruction(string ilVar, string instVar, OpCode opCode, object operand = null) + protected string CreateCilInstruction(IlContext ilVar, string instVar, OpCode opCode, object operand = null) { var operandStr = operand == null ? string.Empty : $", {operand}"; - AddCecilExpression($"var {instVar} = {ilVar}.Create({opCode.ConstantName()}{operandStr});"); + AddCecilExpression($"var {instVar} = {ilVar.VariableName}.Create({opCode.ConstantName()}{operandStr});"); return instVar; } - protected void LoadLiteralValue(string ilVar, ITypeSymbol type, string value, UsageResult usageResult, SyntaxNode parent) + protected void LoadLiteralValue(IlContext ilVar, ITypeSymbol type, string value, UsageResult usageResult, SyntaxNode parent) { if (LoadDefaultValueForTypeParameter(ilVar, type, parent)) return; if (type.SpecialType == SpecialType.None && type.IsValueType && type.TypeKind != TypeKind.Pointer || type.SpecialType == SpecialType.System_DateTime) { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Initobj, Context.TypeResolver.ResolveAny(type, ResolveTargetKind.Instruction)); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Initobj, Context.TypeResolver.Resolve(type, ResolveTargetKind.Instruction)); return; } @@ -150,22 +150,23 @@ protected void LoadLiteralValue(string ilVar, ITypeSymbol type, string value, Us } } - private bool LoadDefaultValueForTypeParameter(string ilVar, ITypeSymbol type, SyntaxNode parent) + private bool LoadDefaultValueForTypeParameter(IlContext ilVar, ITypeSymbol type, SyntaxNode parent) { if (type is not ITypeParameterSymbol typeParameterSymbol) return false; - var resolvedType = Context.TypeResolver.ResolveAny(type, ResolveTargetKind.Instruction); + var resolvedType = Context.TypeResolver.Resolve(type, ResolveTargetKind.Instruction); // in an assignment expression we already have memory allocated to hold the value // in this case we don´t need to add a local variable. if (parent is AssignmentExpressionSyntax assignment) { var targetOfAssignmentSymbol = Context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol.EnsureNotNull(); - var loadAddressOpcode = targetOfAssignmentSymbol.LoadAddressOpcodeForMember(); // target of assignment may be a local, field or parameter so we need to figure out the correct opcode to load its address + //var loadAddressOpcode = targetOfAssignmentSymbol.LoadAddressOpcodeForMember(); // target of assignment may be a local, field or parameter so we need to figure out the correct opcode to load its address + var loadAddressDetails = targetOfAssignmentSymbol.LoadAddressDetailsForForMember(); // target of assignment may be a local, field or parameter so we need to figure out the correct opcode to load its address var storageVariable = Context.DefinitionVariables.GetVariable(targetOfAssignmentSymbol.Name, targetOfAssignmentSymbol.ToVariableMemberKind(), targetOfAssignmentSymbol.Kind == SymbolKind.Local ? string.Empty : targetOfAssignmentSymbol.ContainingSymbol.ToDisplayString()); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadAddressOpcode, storageVariable.VariableName); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadAddressDetails.OpCode, loadAddressDetails.Factory(storageVariable.VariableName)); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Initobj, resolvedType); } else if (parent.Parent is VariableDeclaratorSyntax equalsValueClauseSyntax) @@ -180,7 +181,7 @@ private bool LoadDefaultValueForTypeParameter(string ilVar, ITypeSymbol type, Sy else { // no variable exists yet (for instance, passing `default(T)` as a parameter) so we add one. - var storageVariable = Context.AddLocalVariableToCurrentMethod(type.Name, Context.TypeResolver.ResolveAny(type, ResolveTargetKind.LocalVariable)); + var storageVariable = Context.AddLocalVariableToCurrentMethod(type.Name, Context.TypeResolver.Resolve(type, ResolveTargetKind.LocalVariable)); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, storageVariable.VariableName); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Initobj, resolvedType); @@ -198,7 +199,7 @@ private bool LoadDefaultValueForTypeParameter(string ilVar, ITypeSymbol type, Sy return true; } - private void LoadLiteralToStackHandlingCallOnValueTypeLiterals(string ilVar, ITypeSymbol literalType, object literalValue, UsageResult usageResult) + private void LoadLiteralToStackHandlingCallOnValueTypeLiterals(IlContext ilVar, ITypeSymbol literalType, object literalValue, UsageResult usageResult) { var opCode = literalType.LoadOpCodeFor(); Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode, literalType.ToCilOperandValue(literalValue)); @@ -208,7 +209,7 @@ private void LoadLiteralToStackHandlingCallOnValueTypeLiterals(string ilVar, ITy if (!usageResult.Target.IsVirtual && SymbolEqualityComparer.Default.Equals(usageResult.Target.ContainingType, Context.RoslynTypeSystem.SystemObject)) { Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, tempLocalName); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.ResolveAny(literalType, ResolveTargetKind.Instruction)); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.Resolve(literalType, ResolveTargetKind.Instruction)); } else Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, tempLocalName.AsLocalVariable()); @@ -225,9 +226,9 @@ protected INamedTypeSymbol DeclaredSymbolFor(TypeDeclarationSyntax node) return Context.GetDeclaredSymbol(node).EnsureNotNull(); } - protected void WithCurrentMethod(string declaringTypeName, string localVariable, string methodName, string[] paramTypes, int typeParameterCount, Action action) + protected void WithCurrentMethod(string declaringTypeName, string localVariable, string methodName, string[] paramTypes, string[] typeParameters, Action action) { - using (Context.DefinitionVariables.WithCurrentMethod(declaringTypeName, methodName, paramTypes, typeParameterCount, localVariable)) + using (Context.DefinitionVariables.WithCurrentMethod(declaringTypeName, methodName, paramTypes, typeParameters, localVariable)) { action(methodName); } @@ -235,8 +236,7 @@ protected void WithCurrentMethod(string declaringTypeName, string localVariable, protected string TypeModifiersToCecil(INamedTypeSymbol typeSymbol, SyntaxTokenList modifiers) => Context.ApiDefinitionsFactory.MappedTypeModifiersFor(typeSymbol, modifiers); - //TODO: Probably we need to abstract this one also - internal static string ModifiersToCecil( + internal static string ModifiersAsString( IEnumerable modifiers, string defaultAccessibility, Func> mapAttribute) where TEnumAttr : Enum @@ -252,12 +252,12 @@ internal static string ModifiersToCecil( var modifierStr = finalModifierList .SelectMany(mapAttribute) .Where(attr => !string.IsNullOrEmpty(attr)) - .Aggregate(new StringBuilder(), (acc, curr) => acc.AppendModifier($"{targetEnum}.{curr}")); + .Aggregate(new StringBuilder(), (acc, curr) => acc.AppendEnumFlag($"{targetEnum}.{curr}")); modifierStr.Append(accessibilityModifiers); if (!modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword) || m.IsKind(SyntaxKind.InternalKeyword) || m.IsKind(SyntaxKind.PrivateKeyword) || m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.ProtectedKeyword))) - modifierStr.AppendModifier($"{targetEnum}.{defaultAccessibility}"); + modifierStr.AppendEnumFlag($"{targetEnum}.{defaultAccessibility}"); return modifierStr.ToString(); @@ -285,11 +285,6 @@ bool HandleModifiers(List tokens, SyntaxKind first, SyntaxKind seco } } - protected static void WriteCecilExpression(IVisitorContext context, CecilifierInterpolatedStringHandler value) - { - WriteCecilExpression(context, value.Result); - } - private static void WriteCecilExpression(IVisitorContext context, string value) { context.Generate(value); @@ -300,19 +295,24 @@ protected ResolvedType ResolveExpressionType(ExpressionSyntax expression, Resolv { var typeInfo = Context.GetTypeInfo(expression); var type = (typeInfo.Type ?? typeInfo.ConvertedType).EnsureNotNull(); - return Context.TypeResolver.ResolveAny(type, new TypeResolutionContext(resolveTargetKind, type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); + return Context.TypeResolver.Resolve(type, new TypeResolutionContext(resolveTargetKind, type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None)); } protected ResolvedType ResolveType(TypeSyntax type, ResolveTargetKind resolveTargetKind) { - var resolvedType = Context.TypeResolver.ResolveAny(ResolveTypeSymbol(type), resolveTargetKind); - //TODO: Can't this check be moved inside the Resolve() method as the other checks for arrays, - return type is RefTypeSyntax ? resolvedType.MakeByReferenceType() : resolvedType; + // RefTypeSyntax nodes do not have an ITypeSymbol representation. Instead, they are mapped to + // specific properties in other symbol types; for instance, the information for a `ref return` + // method is mapped in IMethodSymbol.ReturnsByRef property and trying to get the symbol info + // for such types fails whence we `unwrap` (remove the `ref` information) the type, resolve + // the symbol and then `wrap` the result. + var isByRef = type is RefTypeSyntax; + var resolvedType = Context.TypeResolver.Resolve(ResolveTypeSymbol(type), new TypeResolutionContext(resolveTargetKind, isByRef ? TypeResolutionOptions.IsByRef : TypeResolutionOptions.None)); + return isByRef ? Context.TypeResolver.MakeByRefType(resolvedType) : resolvedType; } protected ITypeSymbol ResolveTypeSymbol(TypeSyntax type) { - // Special case types that Context.GetTypeInfo() is not able to handle. As of Oct/2024 only the ones below are requires such special handling. + // Special case types that Context.GetTypeInfo() is not able to handle. As of Jun/2026 only the ones below require such special handling. var typeToCheck = type switch { RefTypeSyntax refType => refType.Type, @@ -329,7 +329,7 @@ protected ITypeSymbol ResolveTypeSymbol(TypeSyntax type) } - protected void ProcessParameter(string ilVar, SimpleNameSyntax node, IParameterSymbol paramSymbol) + protected void ProcessParameter(IlContext ilVar, SimpleNameSyntax node, IParameterSymbol paramSymbol) { var method = (IMethodSymbol) paramSymbol.ContainingSymbol; //TODO: Investigate whether we should/could extract the logic to get a valid method name to an extension method and @@ -342,10 +342,12 @@ protected void ProcessParameter(string ilVar, SimpleNameSyntax node, IParameterS if (HandleLoadAddress(ilVar, paramSymbol.Type, node, OpCodes.Ldarga, operand.AsToken())) return; - if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, paramSymbol.Type, node, OpCodes.Ldarga_S, paramSymbol.Name, VariableMemberKind.Parameter, declaringMethodName)) + var parameterVariable = Context.DefinitionVariables.GetVariable(paramSymbol.Name, VariableMemberKind.Parameter, declaringMethodName); + // We should not validate the parameterVariable here because paramSymbol.Type may not be an 'InlineArray' in which case the variable is not expected to be defined. + if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, paramSymbol.Type, node, OpCodes.Ldarga_S, parameterVariable.VariableName.AsToken())) return; - Utils.EnsureNotNull(node.Parent); + node.Parent.EnsureNotNull(); // We only support non-capturing lambda expressions so we handle those as static (even if the code does not mark them explicitly as such) // if/when we decide to support lambdas that captures variables/fields/params/etc we will probably need to revisit this. var adjustedParameterIndex = paramSymbol.Ordinal + (method.IsStatic || method.MethodKind == MethodKind.AnonymousFunction || method.MethodKind == MethodKind.LocalFunction ? 0 : 1); @@ -363,7 +365,7 @@ protected void ProcessParameter(string ilVar, SimpleNameSyntax node, IParameterS HandlePotentialRefLoad(ilVar, node, paramSymbol.Type); } - protected void ProcessField(string ilVar, SimpleNameSyntax node, IFieldSymbol fieldSymbol) + protected void ProcessField(IlContext ilVar, SimpleNameSyntax node, IFieldSymbol fieldSymbol) { var nodeParent = (CSharpSyntaxNode) node.Parent; Debug.Assert(nodeParent != null); @@ -403,13 +405,15 @@ protected void ProcessField(string ilVar, SimpleNameSyntax node, IFieldSymbol fi HandlePotentialRefLoad(ilVar, node, fieldSymbol.Type); } - protected void ProcessLocalVariable(string ilVar, SimpleNameSyntax localVarSyntax, ILocalSymbol symbol) + protected void ProcessLocalVariable(IlContext ilVar, SimpleNameSyntax localVarSyntax, ILocalSymbol symbol) { var operand = Context.DefinitionVariables.GetVariable(symbol.Name, VariableMemberKind.LocalVariable).VariableName; if (HandleLoadAddress(ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca, operand.AsLocalVariable())) return; - if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca_S, symbol.Name, VariableMemberKind.LocalVariable)) + var localVariableVar = Context.DefinitionVariables.GetVariable(symbol.Name, VariableMemberKind.LocalVariable); + localVariableVar.ThrowIfVariableIsNotValid($"Variable definition for local variable {symbol.Name} from {symbol.ContainingSymbol.ToDisplayString()} not found."); + if (InlineArrayProcessor.HandleInlineArrayConversionToSpan(Context, ilVar, symbol.Type, localVarSyntax, OpCodes.Ldloca_S, localVariableVar.VariableName.AsLocalVariable())) return; Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, new CilLocalVariableHandle(operand)); @@ -418,7 +422,7 @@ protected void ProcessLocalVariable(string ilVar, SimpleNameSyntax localVarSynta HandlePotentialFixedLoad(ilVar, symbol); HandlePotentialRefLoad(ilVar, localVarSyntax, symbol.Type); } - private void HandlePotentialFixedLoad(string ilVar, ILocalSymbol symbol) + private void HandlePotentialFixedLoad(IlContext ilVar, ILocalSymbol symbol) { if (!symbol.IsFixed) return; @@ -426,7 +430,7 @@ private void HandlePotentialFixedLoad(string ilVar, ILocalSymbol symbol) Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Conv_U); } - protected bool HandleLoadAddress(string ilVar, ITypeSymbol loadedType, CSharpSyntaxNode node, OpCode loadOpCode, TOperand operand) + protected bool HandleLoadAddress(IlContext ilVar, ITypeSymbol loadedType, CSharpSyntaxNode node, OpCode loadOpCode, TOperand operand) { var parentNode = (CSharpSyntaxNode)node.Parent; return HandleCallOnTypeParameter() || HandleCallOnValueType() || HandleRefAssignment() || HandleParameter() || HandleInlineArrayElementAccess(); @@ -465,7 +469,7 @@ bool HandleCallOnValueType() operand = default; Context.ApiDriver.WriteCilInstruction(Context, ilVar, ordinaryLoad, operand); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.ResolveAny(loadedType, ResolveTargetKind.Instruction)); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Box, Context.TypeResolver.Resolve(loadedType, ResolveTargetKind.Instruction)); } else Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadOpCode, operand); @@ -476,7 +480,7 @@ bool HandleCallOnValueType() // calls to virtual methods on custom value types needs to be constrained (don't know why, but the generated IL for such scenarios does `constrains`). // the only methods that falls into this category are virtual methods on Object (ToString()/Equals()/GetHashCode()) if (usageResult.Target is { IsOverride: true } && usageResult.Target.ContainingType.IsNonPrimitiveValueType(Context)) - Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.ResolveAny(loadedType, ResolveTargetKind.Instruction).Expression); + Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.Resolve(loadedType, ResolveTargetKind.Instruction).Expression); return true; } @@ -500,7 +504,7 @@ bool HandleCallOnTypeParameter() } Context.ApiDriver.WriteCilInstruction(Context, ilVar, loadOpCode, operand); - Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.ResolveAny(loadedType, ResolveTargetKind.Instruction).Expression); + Context.SetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, Context.TypeResolver.Resolve(loadedType, ResolveTargetKind.TypeReference).Expression); return true; } @@ -542,7 +546,7 @@ bool HandleInlineArrayElementAccess() bool IsPseudoAssignmentToValueType() => Context.HasFlag(Constants.ContextFlags.PseudoAssignmentToIndex); } - protected void HandlePotentialRefLoad(string ilVar, SyntaxNode expression, ITypeSymbol type) + protected void HandlePotentialRefLoad(IlContext ilVar, SyntaxNode expression, ITypeSymbol type) { var needsLoadIndirect = false; @@ -586,7 +590,10 @@ protected void HandlePotentialRefLoad(string ilVar, SyntaxNode expression, IType if (needsLoadIndirect) { var opCode = type.LdindOpCodeFor(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode, opCode == OpCodes.Ldobj ? Context.TypeResolver.ResolveAny(type, ResolveTargetKind.Instruction) : null); + if (opCode == OpCodes.Ldobj) + Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode, Context.TypeResolver.Resolve(type, ResolveTargetKind.TypeReference).AsToken()); + else + Context.ApiDriver.WriteCilInstruction(Context, ilVar, opCode, (string) null); } } @@ -641,7 +648,7 @@ private bool IsSystemIndexUsedAsIndex(ITypeSymbol symbol, SyntaxNode node) return node.Parent.IsKind(SyntaxKind.BracketedArgumentList); } - private void HandlePotentialDelegateInvocationOn(SimpleNameSyntax node, ITypeSymbol typeSymbol, string ilVar) + private void HandlePotentialDelegateInvocationOn(SimpleNameSyntax node, ITypeSymbol typeSymbol, IlContext ilVar) { var invocation = node.Parent as InvocationExpressionSyntax; if (invocation == null || invocation.Expression != node) @@ -652,7 +659,7 @@ private void HandlePotentialDelegateInvocationOn(SimpleNameSyntax node, ITypeSym if (typeSymbol is IFunctionPointerTypeSymbol functionPointer) { var operand = CecilDefinitionsFactory.CallSite(Context.TypeResolver, functionPointer); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Calli, operand); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Calli, operand.AsToken()); return; } @@ -663,7 +670,7 @@ private void HandlePotentialDelegateInvocationOn(SimpleNameSyntax node, ITypeSym : ((IMethodSymbol) typeSymbol.GetMembers("Invoke").SingleOrDefault()).MethodResolverExpression(Context); OnLastInstructionLoadingTargetOfInvocation(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Callvirt, resolvedMethod); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Callvirt, resolvedMethod.AsToken()); } /// @@ -672,18 +679,18 @@ private void HandlePotentialDelegateInvocationOn(SimpleNameSyntax node, ITypeSym /// protected virtual void OnLastInstructionLoadingTargetOfInvocation() { } - protected void HandleAttributesInMemberDeclaration(in SyntaxList nodeAttributeLists, Func predicate, SyntaxKind toMatch, string whereToAdd, VariableMemberKind targetKind) + protected void HandleAttributesInMemberDeclaration(string memberName, in SyntaxList nodeAttributeLists, Func predicate, SyntaxKind toMatch, string whereToAdd, VariableMemberKind targetKind) { var attributeLists = nodeAttributeLists.Where(c => predicate(c.Target, toMatch)); - HandleAttributesInMemberDeclaration(attributeLists, whereToAdd, targetKind); + HandleAttributesInMemberDeclaration(memberName, attributeLists, whereToAdd, targetKind); } protected static bool TargetDoesNotMatch(AttributeTargetSpecifierSyntax target, SyntaxKind operand) => target == null || !target.Identifier.IsKind(operand); protected static bool TargetMatches(AttributeTargetSpecifierSyntax target, SyntaxKind operand) => target != null && target.Identifier.IsKind(operand); - protected void HandleAttributesInMemberDeclaration(IEnumerable attributeLists, string varName, VariableMemberKind targetKind) + protected void HandleAttributesInMemberDeclaration(string memberName, IEnumerable attributeLists, string varName, VariableMemberKind targetKind) { - HandleAttributesInMemberDeclaration(Context, attributeLists, varName, targetKind); + HandleAttributesInMemberDeclaration(Context, memberName, attributeLists, varName, targetKind); } protected static void HandleAttributesInTypeParameter(IVisitorContext context, IEnumerable typeParameters) @@ -694,20 +701,20 @@ protected static void HandleAttributesInTypeParameter(IVisitorContext context, I var parentName = symbol.TypeParameterKind == TypeParameterKind.Method ? symbol.DeclaringMethod?.OriginalDefinition.ToDisplayString() : symbol.DeclaringType?.OriginalDefinition.ToDisplayString(); var typeParamVariable = context.DefinitionVariables.GetVariable(typeParameter.Identifier.Text, VariableMemberKind.TypeParameter, parentName); if (!typeParamVariable.IsValid) - throw new Exception($"Failed to find variable for {parentName}.{symbol.FullyQualifiedName()}"); + throw new Exception($"Failed to find variable for type parameter '{parentName}.{symbol.FullyQualifiedName()}'"); - HandleAttributesInMemberDeclaration(context, typeParameter.AttributeLists, typeParamVariable.VariableName, VariableMemberKind.TypeParameter); + HandleAttributesInMemberDeclaration(context, typeParameter.Identifier.Text, typeParameter.AttributeLists, typeParamVariable.VariableName, VariableMemberKind.TypeParameter); } } - private static void HandleAttributesInMemberDeclaration(IVisitorContext context, IEnumerable attributeLists, string targetDeclarationVar, VariableMemberKind targetKind) + private static void HandleAttributesInMemberDeclaration(IVisitorContext context, string memberName, IEnumerable attributeLists, string targetDeclarationVar, VariableMemberKind targetKind) { foreach (var attribute in attributeLists.SelectMany(al => al.Attributes)) { var attrType = context.SemanticModel.GetTypeInfo(attribute).Type.EnsureNotNull(); var attrsExp = attrType.AttributeKind() switch { - AttributeKind.DllImport => ProcessDllImportAttribute(context, attribute, targetDeclarationVar), + AttributeKind.DllImport => ProcessDllImportAttribute(context, memberName, attribute, targetDeclarationVar), AttributeKind.StructLayout => ProcessStructLayoutAttribute(attribute, targetDeclarationVar), _ => ProcessNormalMemberAttribute(context, attribute, targetDeclarationVar, targetKind) }; @@ -716,110 +723,10 @@ private static void HandleAttributesInMemberDeclaration(IVisitorContext context, } } - private static IEnumerable ProcessDllImportAttribute(IVisitorContext context, AttributeSyntax attribute, string methodVar) + private static IEnumerable ProcessDllImportAttribute(IVisitorContext context, string methodName, AttributeSyntax attribute, string methodVar) { - var moduleName = attribute.ArgumentList?.Arguments.First().ToFullString(); - var existingModuleVar = context.DefinitionVariables.GetVariable(moduleName, VariableMemberKind.ModuleReference); - - var moduleVar = existingModuleVar.IsValid - ? existingModuleVar.VariableName - : context.Naming.SyntheticVariable("dllImportModule", ElementKind.LocalVariable); - - var exps = new List - { - $"{methodVar}.PInvokeInfo = new PInvokeInfo({ PInvokeAttributesFrom(attribute) }, { EntryPoint() }, {moduleVar});", - $"{methodVar}.Body = null;", - $"{methodVar}.ImplAttributes = {MethodImplAttributes()};", - }; - - if (!existingModuleVar.IsValid) - { - exps.InsertRange(0, new[] - { - $"var {moduleVar} = new ModuleReference({moduleName});", - $"assembly.MainModule.ModuleReferences.Add({moduleVar});", - }); - } - - context.DefinitionVariables.RegisterNonMethod("", moduleName, VariableMemberKind.ModuleReference, moduleVar); - - return exps; - - string EntryPoint() => attribute.ArgumentList?.Arguments.FirstOrDefault(arg => arg.NameEquals?.Name.Identifier.Text == "EntryPoint")?.Expression.ToString() ?? "\"\""; - - string MethodImplAttributes() - { - var preserveSig = Boolean.Parse(AttributePropertyOrDefaultValue(attribute, "PreserveSig", "true")); - return preserveSig - ? "MethodImplAttributes.PreserveSig | MethodImplAttributes.Managed" - : "MethodImplAttributes.Managed"; - } - - StringBuilder CallingConventionFrom(AttributeSyntax attr) - { - var callConventionSpan = (attr.ArgumentList?.Arguments.FirstOrDefault(arg => arg.NameEquals?.Name.Identifier.Text == "CallingConvention")?.Expression.ToFullString() - ?? "Winapi").AsSpan(); - - // ensures we use the enum member simple name; Parse() fails if we pass a qualified enum member - var index = callConventionSpan.LastIndexOf('.'); - callConventionSpan = callConventionSpan.Slice(index + 1); - - return new StringBuilder(CallingConventionToCecil(Enum.Parse(callConventionSpan))); - } - - string CharSetFrom(AttributeSyntax attr) - { - var enumMemberName = AttributePropertyOrDefaultValue(attr, "CharSet", "None").AsSpan(); - - // Only use the actual enum member name Parse() fails if we pass a qualified enum member - var index = enumMemberName.LastIndexOf('.'); - enumMemberName = enumMemberName.Slice(index + 1); - - var charSet = Enum.Parse(enumMemberName); - return charSet == CharSet.None ? string.Empty : $"PInvokeAttributes.CharSet{charSet}"; - } - - string SetLastErrorFrom(AttributeSyntax attr) - { - var setLastError = bool.Parse(AttributePropertyOrDefaultValue(attr, "SetLastError", "false")); - return setLastError ? "PInvokeAttributes.SupportsLastError" : string.Empty; - } - - string ExactSpellingFrom(AttributeSyntax attr) - { - var exactSpelling = bool.Parse(AttributePropertyOrDefaultValue(attr, "ExactSpelling", "false")); - return exactSpelling ? "PInvokeAttributes.NoMangle" : string.Empty; - } - - string BestFitMappingFrom(AttributeSyntax attr) - { - var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(attr, "BestFitMapping", "true")); - return bestFitMapping ? "PInvokeAttributes.BestFitEnabled" : "PInvokeAttributes.BestFitDisabled"; - } - - string ThrowOnUnmappableCharFrom(AttributeSyntax attr) - { - var bestFitMapping = bool.Parse(AttributePropertyOrDefaultValue(attr, "ThrowOnUnmappableChar", "false")); - return bestFitMapping ? "PInvokeAttributes.ThrowOnUnmappableCharEnabled" : "PInvokeAttributes.ThrowOnUnmappableCharDisabled"; - } - - // For more information and default values see - // https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportattribute - string PInvokeAttributesFrom(AttributeSyntax attr) - { - return CallingConventionFrom(attr) - .AppendModifier(CharSetFrom(attr)) - .AppendModifier(SetLastErrorFrom(attr)) - .AppendModifier(ExactSpellingFrom(attr)) - .AppendModifier(BestFitMappingFrom(attr)) - .AppendModifier(ThrowOnUnmappableCharFrom(attr)) - .ToString(); - } - - string AttributePropertyOrDefaultValue(AttributeSyntax attr, string propertyName, string defaultValue) - { - return attr.ArgumentList?.Arguments.FirstOrDefault(arg => arg.NameEquals?.Name.Identifier.Text == propertyName)?.Expression.ToFullString() ?? defaultValue; - } + var moduleName = ((LiteralExpressionSyntax)attribute.ArgumentList!.Arguments.First().Expression).Token.ValueText; + return context.ApiDefinitionsFactory.PInvoke(context, moduleName, methodVar, methodName, attribute.ArgumentList.ToCustomAttributeArguments(context).ToArray()); } private static IEnumerable ProcessStructLayoutAttribute(AttributeSyntax attribute, string typeVar) @@ -842,22 +749,6 @@ static int AssignedValue(AttributeSyntax attribute, string parameterName) } } - private static string CallingConventionToCecil(CallingConvention callingConvention) - { - var pinvokeAttribute = callingConvention switch - { - CallingConvention.Cdecl => PInvokeAttributes.CallConvCdecl, - CallingConvention.Winapi => PInvokeAttributes.CallConvWinapi, - CallingConvention.FastCall => PInvokeAttributes.CallConvFastcall, - CallingConvention.StdCall => PInvokeAttributes.CallConvStdCall, - CallingConvention.ThisCall => PInvokeAttributes.CallConvThiscall, - - _ => throw new Exception($"Unexpected calling convention: {callingConvention}") - }; - - return $"PInvokeAttributes.{pinvokeAttribute.ToString()}"; - } - private static IEnumerable ProcessNormalMemberAttribute(IVisitorContext context, AttributeSyntax attribute, string targetDeclarationVar, VariableMemberKind targetKind) { var attributeCtor = context.SemanticModel.GetSymbolInfo(attribute).Symbol.EnsureNotNull(); @@ -894,8 +785,7 @@ private void LogWarning(string message, SyntaxNode node) protected void AddToOverridenMethodsIfAppropriated(string methodVar, IMethodSymbol method) { var overridenMethod = GetOverridenMethod(method); - if (overridenMethod != null) - WriteCecilExpression(Context, $"{methodVar}.Overrides.Add({overridenMethod});"); + Context.ApiDefinitionsFactory.OverrideBaseMethod(Context, methodVar, overridenMethod); } protected string GetOverridenMethod(IMethodSymbol method) diff --git a/Cecilifier.Core/AST/TypeDeclarationVisitor.Delegate.cs b/Cecilifier.Core/AST/TypeDeclarationVisitor.Delegate.cs index 24fa2928..c3a0aa12 100644 --- a/Cecilifier.Core/AST/TypeDeclarationVisitor.Delegate.cs +++ b/Cecilifier.Core/AST/TypeDeclarationVisitor.Delegate.cs @@ -32,7 +32,7 @@ public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) Context, definitionContext, delegateSymbol.ContainingNamespace?.FullyQualifiedName() ?? string.Empty, - CecilDefinitionsFactory.DefaultTypeAttributeFor(TypeKind.Delegate, false).AppendModifier(accessibility), + CecilDefinitionsFactory.DefaultTypeAttributeFor(TypeKind.Delegate, false).AppendEnumFlag(accessibility), Context.RoslynTypeSystem.SystemMulticastDelegate, false, [], @@ -40,7 +40,7 @@ public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) []); AddCecilExpressions(Context, typeDef); - HandleAttributesInMemberDeclaration(node.AttributeLists, typeVar, VariableMemberKind.Type); + HandleAttributesInMemberDeclaration(node.Identifier.Text, node.AttributeLists, typeVar, VariableMemberKind.Type); using (Context.DefinitionVariables.WithCurrent(delegateSymbol.ContainingSymbol?.OriginalDefinition.ToDisplayString() ?? string.Empty, delegateSymbol.OriginalDefinition.ToDisplayString(), VariableMemberKind.Type, typeVar)) { @@ -88,7 +88,7 @@ public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) Constants.Cecil.DelegateMethodAttributes, [new ParameterSpec("ar", Context.TypeResolver.Bcl.System.IAsyncResult, RefKind.None, Constants.ParameterAttributes.None)], [], - ctx => ctx.TypeResolver.ResolveAny(Context.GetTypeInfo(node.ReturnType).Type, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(Context.GetTypeInfo(node.ReturnType).Type, ResolveTargetKind.ReturnType), out var _); endInvokeExps = endInvokeExps.Concat([$"{endInvokeMethodVar}.HasThis = true;", $"{endInvokeMethodVar}.IsRuntime = true;"]); diff --git a/Cecilifier.Core/AST/TypeDeclarationVisitor.cs b/Cecilifier.Core/AST/TypeDeclarationVisitor.cs index 4dc77b4b..6b098570 100644 --- a/Cecilifier.Core/AST/TypeDeclarationVisitor.cs +++ b/Cecilifier.Core/AST/TypeDeclarationVisitor.cs @@ -7,8 +7,6 @@ using Cecilifier.Core.CodeGeneration; using Cecilifier.Core.Extensions; using Cecilifier.Core.Mappings; -using Cecilifier.Core.Naming; -using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -145,7 +143,7 @@ private void HandleTypeDeclaration(TypeDeclarationSyntax node, string varName) AddTypeDefinition(Context, varName, typeSymbol, node.Modifiers, node.TypeParameterList?.Parameters, node.CollectOuterTypeArguments()); } - HandleAttributesInMemberDeclaration(node.AttributeLists, varName, VariableMemberKind.Type); + HandleAttributesInMemberDeclaration(typeSymbol.Name, node.AttributeLists, varName, VariableMemberKind.Type); NonCapturingLambdaProcessor.InjectSyntheticMethodsForNonCapturingLambdas(Context, node, varName); diff --git a/Cecilifier.Core/AST/ValueTypeNoArgCtorInvocationVisitor.cs b/Cecilifier.Core/AST/ValueTypeNoArgCtorInvocationVisitor.cs index 87ac4ca4..48a93c78 100644 --- a/Cecilifier.Core/AST/ValueTypeNoArgCtorInvocationVisitor.cs +++ b/Cecilifier.Core/AST/ValueTypeNoArgCtorInvocationVisitor.cs @@ -32,10 +32,10 @@ namespace Cecilifier.Core.AST internal class ValueTypeNoArgCtorInvocationVisitor : SyntaxWalkerBase { private readonly SymbolInfo ctorInfo; - private readonly string ilVar; + private readonly IlContext ilVar; private readonly BaseObjectCreationExpressionSyntax objectCreationExpressionSyntax; - internal ValueTypeNoArgCtorInvocationVisitor(IVisitorContext ctx, string ilVar, BaseObjectCreationExpressionSyntax objectCreationExpressionSyntax, SymbolInfo ctorInfo) : base(ctx) + internal ValueTypeNoArgCtorInvocationVisitor(IVisitorContext ctx, IlContext ilVar, BaseObjectCreationExpressionSyntax objectCreationExpressionSyntax, SymbolInfo ctorInfo) : base(ctx) { this.ctorInfo = ctorInfo; this.ilVar = ilVar; @@ -47,7 +47,7 @@ public override void VisitUsingStatement(UsingStatementSyntax node) // our direct parent is a using statement, which means we have something like: // using(new Value()) {} var valueTypeLocalVariable = DeclareAndInitializeValueTypeLocalVariable(); - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, valueTypeLocalVariable.VariableName); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloc, valueTypeLocalVariable.VariableName.AsLocalVariable()); } public override void VisitEqualsValueClause(EqualsValueClauseSyntax node) @@ -138,7 +138,7 @@ public override void VisitArgument(ArgumentSyntax node) private DefinitionVariable DeclareAndInitializeValueTypeLocalVariable() { - var tempLocal = Context.AddLocalVariableToCurrentMethod("vt", Context.TypeResolver.ResolveAny(ctorInfo.Symbol.ContainingType, ResolveTargetKind.LocalVariable)); + var tempLocal = Context.AddLocalVariableToCurrentMethod("vt", Context.TypeResolver.Resolve(ctorInfo.Symbol.ContainingType, ResolveTargetKind.LocalVariable)); using var _ = Context.DefinitionVariables.WithVariable(tempLocal); switch (ctorInfo.Symbol.ContainingType.SpecialType) @@ -168,7 +168,7 @@ private DefinitionVariable DeclareAndInitializeValueTypeLocalVariable() private void InitValueTypeLocalVariable(string localVariable) { - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, localVariable.AsToken()); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, localVariable.AsLocalVariable()); Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Initobj, ResolvedStructType(ResolveTargetKind.Instruction).AsToken()); if (objectCreationExpressionSyntax.Initializer is not null) @@ -179,12 +179,12 @@ private void InitValueTypeLocalVariable(string localVariable) // at this point there's no object reference in the stack (it was consumed by the `Initobj` instruction) // so we push the address of the variable that we just initialised again. Notice that after processing // the initializer we need to pop this reference from the stack again. - Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, localVariable); + Context.ApiDriver.WriteCilInstruction(Context, ilVar, OpCodes.Ldloca_S, localVariable.AsLocalVariable()); ProcessInitializerIfNotNull(Context, ilVar, objectCreationExpressionSyntax.Initializer); } } - internal static void ProcessInitializerIfNotNull(IVisitorContext context, string ilVar, InitializerExpressionSyntax initializer) + internal static void ProcessInitializerIfNotNull(IVisitorContext context, IlContext ilVar, InitializerExpressionSyntax initializer) { if (initializer == null) return; @@ -193,6 +193,6 @@ internal static void ProcessInitializerIfNotNull(IVisitorContext context, string context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Pop); } - private ResolvedType ResolvedStructType(ResolveTargetKind targetKind) => Context.TypeResolver.ResolveAny(ctorInfo.Symbol.ContainingType, targetKind); + private ResolvedType ResolvedStructType(ResolveTargetKind targetKind) => Context.TypeResolver.Resolve(ctorInfo.Symbol.ContainingType, targetKind); } } diff --git a/Cecilifier.Core/ApiDriver/ApiDriverCapabilities.cs b/Cecilifier.Core/ApiDriver/ApiDriverCapabilities.cs new file mode 100644 index 00000000..7cacb9c7 --- /dev/null +++ b/Cecilifier.Core/ApiDriver/ApiDriverCapabilities.cs @@ -0,0 +1,13 @@ +using System; + +namespace Cecilifier.Core.ApiDriver; + +[Flags] +public enum ApiDriverCapabilities +{ + None = 0x0, + RequiresForwardReferences = 1 << 0x1, + + // ApiDriver is required to process ParameterSyntax; some ApiDrivers may process those nodes implicitly + RequiresExplicitParameterSyntaxHandling = 1 << 0x2, +} diff --git a/Cecilifier.Core/ApiDriver/BodiedMemberDefinitionContext.cs b/Cecilifier.Core/ApiDriver/BodiedMemberDefinitionContext.cs index 5b4fde3b..abab7488 100644 --- a/Cecilifier.Core/ApiDriver/BodiedMemberDefinitionContext.cs +++ b/Cecilifier.Core/ApiDriver/BodiedMemberDefinitionContext.cs @@ -1,7 +1,6 @@ #nullable enable using System.Diagnostics; using Cecilifier.Core.AST; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Cecilifier.Core.ApiDriver; diff --git a/Cecilifier.Core/ApiDriver/DefinitionsFactory/IApiDriverDefinitionsFactory.cs b/Cecilifier.Core/ApiDriver/DefinitionsFactory/IApiDriverDefinitionsFactory.cs index 40d0b64c..aa8e9d18 100644 --- a/Cecilifier.Core/ApiDriver/DefinitionsFactory/IApiDriverDefinitionsFactory.cs +++ b/Cecilifier.Core/ApiDriver/DefinitionsFactory/IApiDriverDefinitionsFactory.cs @@ -50,6 +50,21 @@ public IEnumerable Type( public IEnumerable Method(IVisitorContext context, IMethodSymbol methodSymbol, BodiedMemberDefinitionContext bodiedMemberDefinitionContext, string methodName, string methodModifiers, IList typeParameters); + /// Generates the statements using the Api Driver API to emit the specified method. + /// The visitor context used during processing. + /// Details about the method being defined. + /// The name of the declaring type. + /// Modifiers to be applied to the method. + /// List of parameters. + /// In case of generic methods lists all method Type Parameters; empty otherwise. + /// A Fun<T> responsible to resolve the method return's type. + /// the used to store the data representing the just emitted method. This is used when emitting code that references the method. + /// + /// This overload is best suited to emit methods being synthetyzed by an Api Driver, i.e. in scenarios in which the method is not present in the source code; this is most + /// commonly seem when dealing with code that C# compiler 'lowers' before emitting the IL (for instance, for inline arrays C# compiler emits an extra type named + /// <PrivateImplementationDetail> with multiple methods. + /// + /// A list of statements to emit the method using the Api Driver API. public IEnumerable Method(IVisitorContext context, BodiedMemberDefinitionContext definitionContext, string declaringTypeName, @@ -69,4 +84,18 @@ public IEnumerable Method(IVisitorContext context, DefinitionVariable LocalVariable(IVisitorContext context, string variableName, string methodDefinitionVariableName, ResolvedType resolvedType); IEnumerable Property(IVisitorContext context, BodiedMemberDefinitionContext definitionContext, string declaringTypeName, List propertyParameters, ResolvedType propertyType); IEnumerable Attribute(IVisitorContext context, IMethodSymbol attributeCtor, string attributeVarBaseName, string attributeTargetVar, VariableMemberKind targetKind, params CustomAttributeArgument[] arguments); + + IEnumerable Event(IVisitorContext context, BodiedMemberDefinitionContext eventSpec, string declaringTypeName, ResolvedType eventType, string addAccessorVariable, string removeAccessorVariable); + + /// + /// Emits code to override a method from a base class. + /// + /// Visitor context to use. + /// Expression representing the method overriding the base method. Note that commonly this is the name of a variable holding the data representing a method definition. + /// + /// Expression representing the base method being overriden. If this is 'null' does not override any method and whence no code is generated."/> + /// + void OverrideBaseMethod(IVisitorContext context, string overriderMethodVar, string? overridenMethod); + + IEnumerable PInvoke(IVisitorContext context, string moduleName, string methodVar, string methodName, ReadOnlySpan customAttributeArguments); } diff --git a/Cecilifier.Core/ApiDriver/ExceptionHandlerEntry.cs b/Cecilifier.Core/ApiDriver/ExceptionHandlerEntry.cs new file mode 100644 index 00000000..0fcb76f8 --- /dev/null +++ b/Cecilifier.Core/ApiDriver/ExceptionHandlerEntry.cs @@ -0,0 +1,14 @@ +#nullable enable +using Cecilifier.Core.TypeSystem; + +namespace Cecilifier.Core.ApiDriver; + +public enum ExceptionHandlerKind +{ + Catch, + Finally, + Fault, +} + +public record struct ExceptionHandlerEntry(ExceptionHandlerKind Kind, ResolvedType CatchType, string TryStart, string TryEnd, string HandlerStart, string HandlerEnd); + diff --git a/Cecilifier.Core/ApiDriver/IILGeneratorApiDriver.cs b/Cecilifier.Core/ApiDriver/IILGeneratorApiDriver.cs index 9e8155de..06b6a6d4 100644 --- a/Cecilifier.Core/ApiDriver/IILGeneratorApiDriver.cs +++ b/Cecilifier.Core/ApiDriver/IILGeneratorApiDriver.cs @@ -14,6 +14,8 @@ namespace Cecilifier.Core.ApiDriver; /// public interface IILGeneratorApiDriver { + ApiDriverCapabilities DriverCapabilities => ApiDriverCapabilities.None; + string AsCecilApplication(string cecilifiedCode, string mainTypeName, string? entryPointVar); int PreambleLineCount { get; } IReadOnlyCollection AssemblyReferences { get; } @@ -26,9 +28,22 @@ public interface IILGeneratorApiDriver void WriteCilInstruction(IVisitorContext context, IlContext il, OpCode opCode, T? operand, string? comment = null); void WriteCilInstruction(IVisitorContext context, IlContext il, OpCode opCode); void WriteCilBranch(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null); + string EmitCilBranchInstruction(IVisitorContext context, IlContext il, OpCode branchOpCode, string targetLabel, string? comment = null); + /// + /// Define a new label in the IL instruction stream that can be used as a target of instructions such as branch ones. + /// + /// + /// + /// Variable name to be used in future references to the label; for instance, when emitting branches targetting the label. + /// Defining a label simply creates a variable suitable to be used as a target of instructions. The actual instruction referenced by the label needs to be marked by calling . void DefineLabel(IVisitorContext context, IlContext il, string labelVariable); + string EmitDefineLabel(IVisitorContext context, IlContext il, string labelVariable); + void MarkLabel(IVisitorContext context, IlContext il, string labelVariable); + string EmitMarkLabel(IVisitorContext context, IlContext il, string labelVariable); void AddMethodSemantics(IVisitorContext context, string targetVariable, string methodVariable, MethodKind methodKind); + + void WriteExceptionHandlers(IVisitorContext context, IlContext ilVar, IEnumerable exceptionHandlerTable); } diff --git a/Cecilifier.Core/Cecilifier.cs b/Cecilifier.Core/Cecilifier.cs index e88c8684..a79978b5 100644 --- a/Cecilifier.Core/Cecilifier.cs +++ b/Cecilifier.Core/Cecilifier.cs @@ -41,8 +41,6 @@ public static CecilifierResult Process(Stream content, CecilifierOptio var semanticModel = comp.GetSemanticModel(syntaxTree); var context = TContext.CreateContext(options, semanticModel); - IsSRM = typeof(TContext).Name.Contains("SystemReflectionMetadataContext"); - CecilifierInterpolatedStringHandler.BaseIndentation = context.Indentation; var visitor = new CompilationUnitVisitor(context); @@ -54,8 +52,6 @@ public static CecilifierResult Process(Stream content, CecilifierOptio return new CecilifierResult(reader, mainTypeName, context.Mappings, context, context.Diagnostics); } - public static bool IsSRM; - private static OutputKind OutputKindFor(SyntaxTree syntaxTree) { var outputKind = syntaxTree.GetRoot().DescendantNodes().Any(node => node.IsKind(SyntaxKind.GlobalStatement)) diff --git a/Cecilifier.Core/CodeGeneration/PrimaryConstructor.Generator.cs b/Cecilifier.Core/CodeGeneration/PrimaryConstructor.Generator.cs index 712ec75e..a1da81d1 100644 --- a/Cecilifier.Core/CodeGeneration/PrimaryConstructor.Generator.cs +++ b/Cecilifier.Core/CodeGeneration/PrimaryConstructor.Generator.cs @@ -46,7 +46,7 @@ private static void AddPropertyFor(IVisitorContext context, ParameterSyntax para var propDefVar = context.Naming.SyntheticVariable(parameter.Identifier.Text, ElementKind.Property); var paramSymbol = context.SemanticModel.GetDeclaredSymbol(parameter).EnsureNotNull(); var definitionContext = new BodiedMemberDefinitionContext(parameter.Identifier.Text, propDefVar, declaringTypeVariable.VariableName, MemberOptions.None, IlContext.None); - var exps = context.ApiDefinitionsFactory.Property(context, definitionContext, declaringTypeVariable.MemberName, [], context.TypeResolver.ResolveAny(paramSymbol.Type, ResolveTargetKind.None)); + var exps = context.ApiDefinitionsFactory.Property(context, definitionContext, declaringTypeVariable.MemberName, [], context.TypeResolver.Resolve(paramSymbol.Type, ResolveTargetKind.None)); context.Generate(exps); context.Generate($"{typeDefinitionVariable}.Properties.Add({propDefVar});"); @@ -67,7 +67,7 @@ private static void AddPropertyFor(IVisitorContext context, ParameterSyntax para ["set"] = publicPropertyMethodAttributes }, false, - resolveTargetKind => context.TypeResolver.ResolveAny(propertyType, resolveTargetKind), + resolveTargetKind => context.TypeResolver.Resolve(propertyType, resolveTargetKind), propertyType.ToDisplayString(), [], "FieldAttributes.Private", @@ -84,11 +84,9 @@ void AddGetter() context.WriteComment($"{propertyData.Name} getter"); var getMethodVar = context.Naming.SyntheticVariable($"get{propertyData.Name}", ElementKind.Method); // properties for primary ctor parameters cannot override base properties, so hasCovariantReturn = false and overridenMethod = null (none) - var ilVar = context.Naming.ILProcessor($"get{propertyData.Name}"); + var ilVar = context.ApiDriver.NewIlContext(context, $"get{propertyData.Name}", getMethodVar); using (propertyGenerator.AddGetterMethodDeclaration(in propertyData, getMethodVar, false, null, ilVar)) { - context.Generate([$"var {ilVar} = {getMethodVar}.Body.GetILProcessor();"]); - propertyGenerator.AddAutoGetterMethodImplementation(in propertyData, ilVar, getMethodVar); } context.WriteNewLine(); @@ -136,7 +134,7 @@ internal static void AddPrimaryConstructor(IVisitorContext context, string recor var ctorExps = context.ApiDefinitionsFactory.MethodBody(context, $"ctor_{typeDeclaration.Identifier.ValueText}", ilContext, [], []); context.Generate(ctorExps); - var resolvedType = context.TypeResolver.ResolveAny(typeSymbol, ResolveTargetKind.TypeReference); + var resolvedType = context.TypeResolver.Resolve(typeSymbol, ResolveTargetKind.TypeReference); Func fieldRefResolver = backingFieldVar => typeDeclaration.TypeParameterList?.Parameters.Count > 0 ? $"new FieldReference({backingFieldVar}.Name, {backingFieldVar}.FieldType, {resolvedType})" : backingFieldVar; @@ -146,28 +144,28 @@ internal static void AddPrimaryConstructor(IVisitorContext context, string recor { context.WriteComment($"Parameter: {parameter.Identifier}"); var paramVar = context.Naming.Parameter(parameter); - var parameterType = context.TypeResolver.ResolveAny(ModelExtensions.GetTypeInfo(context.SemanticModel, parameter.Type!).Type, ResolveTargetKind.Parameter); - var paramExps = CecilDefinitionsFactory.Parameter(parameter.Identifier.ValueText, RefKind.None, null, ctorVar, paramVar, parameterType, Constants.ParameterAttributes.None, ("", false)); + var parameterType = context.TypeResolver.Resolve(ModelExtensions.GetTypeInfo(context.SemanticModel, parameter.Type!).Type, ResolveTargetKind.Parameter); + var paramExps = CecilDefinitionsFactory.Parameter(context, parameter.Identifier.ValueText, RefKind.None, null, ctorVar, paramVar, parameterType, Constants.ParameterAttributes.None, ("", false)); context.Generate(paramExps); if (!uniqueParameters.Contains(parameter)) continue; - context.ApiDriver.WriteCilInstruction(context, ilContext.VariableName, OpCodes.Ldarg_0); - context.ApiDriver.WriteCilInstruction(context, ilContext.VariableName, OpCodes.Ldarg, paramVar); + context.ApiDriver.WriteCilInstruction(context, ilContext, OpCodes.Ldarg_0); + context.ApiDriver.WriteCilInstruction(context, ilContext, OpCodes.Ldarg, paramVar); var backingFieldVar = context.DefinitionVariables.GetVariable(Utils.BackingFieldNameForAutoProperty(parameter.Identifier.ValueText), VariableMemberKind.Field, typeSymbol.OriginalDefinition.ToDisplayString()); if (!backingFieldVar.IsValid) throw new InvalidOperationException($"Backing field variable for property '{parameter.Identifier.ValueText}' could not be found."); - context.ApiDriver.WriteCilInstruction(context, ilContext.VariableName, OpCodes.Stfld, fieldRefResolver(backingFieldVar.VariableName)); + context.ApiDriver.WriteCilInstruction(context, ilContext, OpCodes.Stfld, fieldRefResolver(backingFieldVar.VariableName)); } if (!typeSymbol.IsValueType) - InvokeBaseConstructor(context, ilContext.VariableName, typeDeclaration); - context.ApiDriver.WriteCilInstruction(context, ilContext.VariableName, OpCodes.Ret); + InvokeBaseConstructor(context, ilContext, typeDeclaration); + context.ApiDriver.WriteCilInstruction(context, ilContext, OpCodes.Ret); - static void InvokeBaseConstructor(IVisitorContext context, string ctorIlVar, TypeDeclarationSyntax typeDeclaration) + static void InvokeBaseConstructor(IVisitorContext context, IlContext ctorIlVar, TypeDeclarationSyntax typeDeclaration) { string baseCtor; context.ApiDriver.WriteCilInstruction(context, ctorIlVar, OpCodes.Ldarg_0); diff --git a/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs b/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs index f598fce1..4ae9ab97 100644 --- a/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs +++ b/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs @@ -69,34 +69,37 @@ private static DefinitionVariable GetOrEmmitInlineArrayAsSpanLikeMethod(IVisitor var methodTypeQualifiedName = $"{privateImplementationDetailsVar.MemberName}.{methodName}"; string declaringTypeName = $"{privateImplementationDetailsVar.MemberName}"; IReadOnlyList parameters = [ - new ParameterSpec("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (context, name) => ResolveOwnedGenericParameter(context, name, methodTypeQualifiedName)), - new ParameterSpec("length", context.TypeResolver.Bcl.System.Int32, RefKind.None, Constants.ParameterAttributes.None) + new("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (context, paramSpec) => ResolveOwnedGenericParameter(context, paramSpec.ElementType.Expression, TypeParameterKind.Method, ResolveTargetKind.Parameter, methodTypeQualifiedName)), + new("length", context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) ]; Func returnTypeResolver = ctx => { - var spanTypeParameter = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName); - return ctx.TypeResolver.ResolveAny(containingType, ResolveTargetKind.ReturnType).MakeGenericInstanceType(spanTypeParameter); + var spanTypeParameter = ResolveOwnedGenericParameter(context, "TElement", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); + return ctx.TypeResolver.MakeGenericInstanceType(containingType.NameIncludingTypeParametersAndArguments(), ctx.TypeResolver.Resolve(containingType, new TypeResolutionContext(ResolveTargetKind.ReturnType, containingType.GetTypeResolutionOptions())), [new ResolvedType(spanTypeParameter)], new TypeResolutionContext(ResolveTargetKind.ReturnType, containingType.GetTypeResolutionOptions())); }; + var ilContext = context.ApiDriver.NewIlContext(context, methodName, methodVar); var methodExpressions = context.ApiDefinitionsFactory.Method( - context, - new BodiedMemberDefinitionContext(methodName, methodTypeQualifiedName,methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.None, IlContext.None), - declaringTypeName, - "MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig", - parameters, - new [] {"TBuffer", "TElement" }, + context, + new BodiedMemberDefinitionContext(methodName, methodTypeQualifiedName,methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.Static, ilContext), + declaringTypeName, + "MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig", + parameters, + ["TBuffer", "TElement"], returnTypeResolver, out var methodDefinitionVariable); context.Generate(methodExpressions); - var tBufferVar = ResolveOwnedGenericParameter(context, "TBuffer", methodTypeQualifiedName); - var tElementVar = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName); + var tBufferVar = ResolveOwnedGenericParameter(context, "TBuffer", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); + var tElementVar = ResolveOwnedGenericParameter(context, "TElement", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); context.WriteComment("Unsafe.As() generic instance method"); - var unsafeAsVar = GetUnsafeAsMethod(context).MethodResolverExpression(context).MakeGenericInstanceMethod(context, "unsafeAs", [tBufferVar, tElementVar]); + IReadOnlyList resolvedTypeArguments = [tBufferVar, tElementVar]; + var unsafeAsVar = context.MemberResolver.MakeGeneticInstanceMethod(GetUnsafeAsMethod(context).MethodResolverExpression(context), "unsafeAs", resolvedTypeArguments); context.WriteComment($"MemoryMarshal.{createSpanMethodName}() generic instance method"); - var memoryMarshalCreateSpanVar = GetMemoryMarshalCreateSpanMethod(context, createSpanMethodName).MethodResolverExpression(context).MakeGenericInstanceMethod(context, "createSpan", [tElementVar]); + IReadOnlyList resolvedTypeArguments1 = [tElementVar]; + var memoryMarshalCreateSpanVar = context.MemberResolver.MakeGeneticInstanceMethod(GetMemoryMarshalCreateSpanMethod(context, createSpanMethodName).MethodResolverExpression(context), "createSpan", resolvedTypeArguments1); InstructionRepresentation[] instructions = [ OpCodes.Ldarg_0, @@ -105,7 +108,7 @@ private static DefinitionVariable GetOrEmmitInlineArrayAsSpanLikeMethod(IVisitor OpCodes.Call.WithOperand(memoryMarshalCreateSpanVar.AsToken()), OpCodes.Ret ]; - var exps = context.ApiDefinitionsFactory.MethodBody(context, methodName, context.ApiDriver.NewIlContext(context, methodName, methodVar), [], instructions); + var exps = context.ApiDefinitionsFactory.MethodBody(context, methodName, ilContext, [], instructions); context.Generate(exps); return methodDefinitionVariable; @@ -148,18 +151,20 @@ public static DefinitionVariable GetOrEmmitInlineArrayFirstElementRefMethod(IVis context.WriteComment($"{Constants.CompilerGeneratedTypes.PrivateImplementationDetails}.InlineArrayFirstElementRef()"); var methodVar = context.Naming.SyntheticVariable("inlineArrayFirstElementRef", ElementKind.Method); - var methodTypeQualifiedName = $"{privateImplementationDetailsVar.MemberName}.InlineArrayFirstElementRef"; + var methodTypeQualifiedName = $"{privateImplementationDetailsVar.MemberName}.InlineArrayFirstElementRef".ToValidIdentifier(); string declaringTypeName = $"{privateImplementationDetailsVar.MemberName}"; - IReadOnlyList parameters = [ new ParameterSpec("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (ctx, name) => ResolveOwnedGenericParameter(ctx, name, methodTypeQualifiedName))]; + IReadOnlyList parameters = [ new("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (ctx, paramSpec) => ResolveOwnedGenericParameter(ctx, paramSpec.ElementType.Expression, TypeParameterKind.Method, ResolveTargetKind.Parameter, methodTypeQualifiedName))]; IList typeParameters = ["TBuffer", "TElement"]; Func returnTypeResolver = ctx => { - var spanTypeParameter = ResolveOwnedGenericParameter(ctx, "TElement", methodTypeQualifiedName); - return new ResolvedType(spanTypeParameter).MakeByReferenceType(); + var spanTypeParameter = ResolveOwnedGenericParameter(ctx, "TElement", TypeParameterKind.Method, ResolveTargetKind.ReturnType, methodTypeQualifiedName); + return context.TypeResolver.MakeByRefType(new ResolvedType(spanTypeParameter)); }; + + var ilContext = context.ApiDriver.NewIlContext(context, "InlineArrayFirstElementRef", methodVar); var methodExpressions = context.ApiDefinitionsFactory.Method( context, - new BodiedMemberDefinitionContext("InlineArrayFirstElementRef", methodTypeQualifiedName, methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.None, IlContext.None), + new BodiedMemberDefinitionContext("InlineArrayFirstElementRef", methodTypeQualifiedName, methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.Static, ilContext), declaringTypeName, "MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig", parameters, @@ -168,20 +173,16 @@ public static DefinitionVariable GetOrEmmitInlineArrayFirstElementRefMethod(IVis out var methodDefinitionVariable); context.Generate(methodExpressions); - var tBufferTypeParameter = ResolveOwnedGenericParameter(context, "TBuffer", methodTypeQualifiedName); - var tElementTypeParameter = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName); - - var unsafeAsVarName = GetUnsafeAsMethod(context) - .MethodResolverExpression(context) - .MakeGenericInstanceMethod(context, "unsafeAs", [tBufferTypeParameter, tElementTypeParameter]); + var tBufferTypeParameter = ResolveOwnedGenericParameter(context, "TBuffer", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); + var tElementTypeParameter = ResolveOwnedGenericParameter(context, "TElement", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); + var unsafeAsVarName = context.MemberResolver.MakeGeneticInstanceMethod(GetUnsafeAsMethod(context).MethodResolverExpression(context), "unsafeAs", [tBufferTypeParameter, tElementTypeParameter]); InstructionRepresentation[] instructions = [ OpCodes.Ldarg_0, OpCodes.Call.WithOperand(unsafeAsVarName.AsToken()), OpCodes.Ret ]; - var ilContext = context.ApiDriver.NewIlContext(context, "UnsafeAs", methodVar); - var exps = context.ApiDefinitionsFactory.MethodBody(context, "UnsafeAs", ilContext, [], instructions); + var exps = context.ApiDefinitionsFactory.MethodBody(context, "InlineArrayFirstElementRef", ilContext, [], instructions); context.Generate(exps); context.WriteNewLine(); context.WriteComment("-------------------------------"); @@ -201,21 +202,22 @@ public static DefinitionVariable GetOrEmmitInlineArrayElementRefMethod(IVisitorC context.WriteComment($"{Constants.CompilerGeneratedTypes.PrivateImplementationDetails}.InlineArrayElementRef()"); var methodVar = context.Naming.SyntheticVariable("inlineArrayElementRef", ElementKind.Method); - var methodTypeQualifiedName = $"{privateImplementationDetailsVar.MemberName}.InlineArrayElementRef"; + var methodTypeQualifiedName = $"{privateImplementationDetailsVar.MemberName}.InlineArrayElementRef".ToValidIdentifier(); string declaringTypeName = $"{privateImplementationDetailsVar.MemberName}"; IReadOnlyList parameters = [ - new ParameterSpec("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (ctx, name) => ResolveOwnedGenericParameter(ctx, name, methodTypeQualifiedName)), - new ParameterSpec("index", context.TypeResolver.Bcl.System.Int32, RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = "int" } + new("buffer", "TBuffer", RefKind.Ref, Constants.ParameterAttributes.None, null, (ctx, paramSpec) => ResolveOwnedGenericParameter(ctx, paramSpec.ElementType.Expression, TypeParameterKind.Method, ResolveTargetKind.Parameter, methodTypeQualifiedName)), + new("index", context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemInt32, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = "int" } ]; IList typeParameters = ["TBuffer", "TElement"]; Func returnTypeResolver = ctx => { - var spanTypeParameter = ResolveOwnedGenericParameter(ctx, "TElement", methodTypeQualifiedName); - return new ResolvedType(spanTypeParameter).MakeByReferenceType(); + var spanTypeParameter = ResolveOwnedGenericParameter(ctx, "TElement", TypeParameterKind.Method, ResolveTargetKind.ReturnType, methodTypeQualifiedName); + return context.TypeResolver.MakeByRefType(new ResolvedType(spanTypeParameter)); }; + var ilContext = context.ApiDriver.NewIlContext(context, "InlineArrayElementRef", methodVar); var methodExpressions = context.ApiDefinitionsFactory.Method( context, - new BodiedMemberDefinitionContext("InlineArrayElementRef", methodTypeQualifiedName, methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.None, IlContext.None), + new BodiedMemberDefinitionContext("InlineArrayElementRef", methodTypeQualifiedName, methodVar, privateImplementationDetailsVar.VariableName, MemberOptions.Static, ilContext), declaringTypeName, "MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig", parameters, @@ -225,17 +227,13 @@ public static DefinitionVariable GetOrEmmitInlineArrayElementRefMethod(IVisitorC context.Generate(methodExpressions); - var tbufferTypeParameter = ResolveOwnedGenericParameter(context, "TBuffer", methodTypeQualifiedName); - var telementTypeParameter = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName); + var tbufferTypeParameter = ResolveOwnedGenericParameter(context, "TBuffer", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); + var telementTypeParameter = ResolveOwnedGenericParameter(context, "TElement", TypeParameterKind.Method, ResolveTargetKind.GenericTypeArgument, methodTypeQualifiedName); - var unsafeAsVarName = GetUnsafeAsMethod(context) - .MethodResolverExpression(context) - .MakeGenericInstanceMethod(context, "unsafeAs", [tbufferTypeParameter, telementTypeParameter]); - - var unsafeAddVarName = GetUnsafeAddMethod(context) - .MethodResolverExpression(context) - .MakeGenericInstanceMethod(context, "unsafeAdd", [telementTypeParameter]); + IReadOnlyList resolvedTypeArguments = [tbufferTypeParameter, telementTypeParameter]; + var unsafeAsVarName = context.MemberResolver.MakeGeneticInstanceMethod(GetUnsafeAsMethod(context).MethodResolverExpression(context), "unsafeAs", resolvedTypeArguments); + var unsafeAddVarName = context.MemberResolver.MakeGeneticInstanceMethod(GetUnsafeAddMethod(context).MethodResolverExpression(context), "unsafeAdd", [telementTypeParameter]); InstructionRepresentation[] instructions = [ OpCodes.Ldarg_0, OpCodes.Call.WithOperand(unsafeAsVarName.AsToken()), @@ -243,7 +241,7 @@ public static DefinitionVariable GetOrEmmitInlineArrayElementRefMethod(IVisitorC OpCodes.Call.WithOperand(unsafeAddVarName.AsToken()), OpCodes.Ret ]; - var methodBodyExpressions = context.ApiDefinitionsFactory.MethodBody(context, "UnsafeAdd", context.ApiDriver.NewIlContext(context, "UnsafeAdd", methodVar), [], instructions); + var methodBodyExpressions = context.ApiDefinitionsFactory.MethodBody(context, "UnsafeAdd", ilContext, [], instructions); context.Generate(methodBodyExpressions); context.WriteNewLine(); @@ -253,14 +251,14 @@ public static DefinitionVariable GetOrEmmitInlineArrayElementRefMethod(IVisitorC return methodDefinitionVariable; } - private static string ResolveOwnedGenericParameter(IVisitorContext context, string name, string methodTypeQualifiedName) + private static string ResolveOwnedGenericParameter(IVisitorContext context, string typeParameterName, TypeParameterKind typeParameterKind, ResolveTargetKind targetKind, string methodTypeQualifiedName) { - var spanTypeParameter = context.DefinitionVariables.GetVariable( - name, - VariableMemberKind.TypeParameter, - methodTypeQualifiedName); - - return spanTypeParameter.VariableName; + var foundVar = context.DefinitionVariables.GetVariable(typeParameterName, VariableMemberKind.TypeParameter, methodTypeQualifiedName); + foundVar.ThrowIfVariableIsNotValid(); + + // In reality this method is invoked to obtain the 'return type' of a method as well as some parameters. In this specific case `ResolveTargetKind.ReturnType` + // works as expected for both scenarios. + return context.TypeResolver.ResolveTypeParameter(foundVar.VariableName, typeParameterKind, new TypeResolutionContext(targetKind, TypeResolutionOptions.IsByRef)).Expression; } internal static string GetOrCreateInitializationBackingFieldVariableName(IVisitorContext context, int elementSizeInBytes, IList elements, SpanAction converter) @@ -315,10 +313,10 @@ internal static string GetOrCreateInitializationBackingFieldVariableName(IVisito private static ResolvedType GetOrCreateRawDataType(IVisitorContext context, long sizeInBytes) { if (sizeInBytes == sizeof(int)) - return context.TypeResolver.Bcl.System.Int32; + return context.TypeResolver.ApplySpecificSyntax(context.TypeResolver.Bcl.System.Int32.Expression, ResolveTargetKind.Field); if (sizeInBytes == sizeof(long)) - return context.TypeResolver.Bcl.System.Int64; + return context.TypeResolver.ApplySpecificSyntax(context.TypeResolver.Bcl.System.Int64.Expression, ResolveTargetKind.Field); var rawDataHolderStructName = Constants.CompilerGeneratedTypes.StaticArrayInitTypeNameFor(sizeInBytes); var found = context.DefinitionVariables.GetVariable(rawDataHolderStructName, VariableMemberKind.Type, Constants.CompilerGeneratedTypes.PrivateImplementationDetails); diff --git a/Cecilifier.Core/CodeGeneration/Property.Generator.cs b/Cecilifier.Core/CodeGeneration/Property.Generator.cs index 702cf7dc..df590207 100644 --- a/Cecilifier.Core/CodeGeneration/Property.Generator.cs +++ b/Cecilifier.Core/CodeGeneration/Property.Generator.cs @@ -63,12 +63,12 @@ internal ScopedDefinitionVariable AddSetterMethodDeclaration(ref readonly Proper property.AccessorModifiers["set"], completeParamList, typeParameters, - ctx => ctx.TypeResolver.ResolveAny(Context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(Context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType), out var methodDefinitionVariable); var methodVariableScope = Context.DefinitionVariables.WithCurrentMethod(methodDefinitionVariable); Context.Generate(exps); - AddToOverridenMethodsIfAppropriated(accessorMethodVar, overridenMethod); + Context.ApiDefinitionsFactory.OverrideBaseMethod(Context, accessorMethodVar, overridenMethod); Context.ApiDriver.AddMethodSemantics(Context, property.Variable, accessorMethodVar, MethodKind.PropertySet); @@ -107,7 +107,7 @@ internal ScopedDefinitionVariable AddGetterMethodDeclaration(ref readonly Proper var scopedVariable = Context.DefinitionVariables.WithCurrentMethod(methodDefinitionVariable); - AddToOverridenMethodsIfAppropriated(accessorMethodVar, overridenMethod); + Context.ApiDefinitionsFactory.OverrideBaseMethod(Context, accessorMethodVar, overridenMethod); Context.Generate([ hasCovariantReturn ? @@ -118,7 +118,7 @@ internal ScopedDefinitionVariable AddGetterMethodDeclaration(ref readonly Proper return scopedVariable; } - internal void AddAutoGetterMethodImplementation(ref readonly PropertyGenerationData propertyGenerationData, string ilVar, string getMethodVar) + internal void AddAutoGetterMethodImplementation(ref readonly PropertyGenerationData propertyGenerationData, IlContext ilVar, string getMethodVar) { AddBackingFieldIfNeeded(in propertyGenerationData); @@ -132,15 +132,6 @@ internal void AddAutoGetterMethodImplementation(ref readonly PropertyGenerationD Context.AddCompilerGeneratedAttributeTo(getMethodVar, VariableMemberKind.Method); } - - private void AddToOverridenMethodsIfAppropriated(string accessorMethodVar, string overridenMethod) - { - if (string.IsNullOrWhiteSpace(overridenMethod)) - return; - - Context.Generate($"{accessorMethodVar}.Overrides.Add({overridenMethod});"); - Context.WriteNewLine(); - } private void AddBackingFieldIfNeeded(ref readonly PropertyGenerationData property) { @@ -159,7 +150,7 @@ private void AddBackingFieldIfNeeded(ref readonly PropertyGenerationData propert private string BackingFieldReferenceOnGenericInstanceType(ref readonly PropertyGenerationData property) { - var closedDeclaringType = Context.TypeResolver.MakeGenericInstanceType(property.DeclaringTypeVariable, property.DeclaringTypeSymbol, new TypeResolutionContext(ResolveTargetKind.TypeReference, property.DeclaringTypeSymbol.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None )); + var closedDeclaringType = Context.TypeResolver.MakeGenericInstanceType(property.DeclaringTypeSymbol.NameIncludingTypeParametersAndArguments(), property.DeclaringTypeVariable, property.DeclaringTypeSymbol, new TypeResolutionContext(ResolveTargetKind.TypeReference, property.DeclaringTypeSymbol.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None )); var fieldRefVar = Context.Naming.MemberReference($"backingField_{property.Name}"); var exps = Context.ApiDefinitionsFactory.FieldReference(Context, fieldRefVar, Utils.BackingFieldNameForAutoProperty(property.Name), property.Type(ResolveTargetKind.Field), in closedDeclaringType); diff --git a/Cecilifier.Core/CodeGeneration/Record.Generator.PrintMembers.cs b/Cecilifier.Core/CodeGeneration/Record.Generator.PrintMembers.cs index 073fafc0..9bb80cf1 100644 --- a/Cecilifier.Core/CodeGeneration/Record.Generator.PrintMembers.cs +++ b/Cecilifier.Core/CodeGeneration/Record.Generator.PrintMembers.cs @@ -27,7 +27,7 @@ private void AddPrintMembersMethod(string stringBuilderAppendStringMethod, IName context.WriteNewLine(); context.WriteComment($"{record.Identifier.ValueText}.{PrintMembersMethodName}()"); - var builderParameter = new ParameterSpec("builder", context.TypeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None); + var builderParameter = new ParameterSpec("builder", context.TypeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None); string methodModifiers = $"MethodAttributes.Family | {(HasBaseRecord(record) ? Constants.Cecil.HideBySigVirtual : Constants.Cecil.HideBySigNewSlotVirtual)}"; Func returnTypeResolver = ctx => ctx.TypeResolver.Bcl.System.Boolean; var printMembersDeclExps = context.ApiDefinitionsFactory.Method( @@ -81,7 +81,7 @@ private void AddPrintMembersMethod(string stringBuilderAppendStringMethod, IName OpCodes.Ldarg_1, OpCodes.Ldarg_0, OpCodes.Call.WithOperand(ClosedGenericMethodFor($"get_{property.Name}", recordTypeDefinitionVariable)), - OpCodes.Box.WithOperand(context.TypeResolver.ResolveAny(property.Type, ResolveTargetKind.None).Expression).IgnoreIf(property.Type.TypeKind != TypeKind.TypeParameter), + OpCodes.Box.WithOperand(context.TypeResolver.Resolve(property.Type, ResolveTargetKind.None).Expression).IgnoreIf(property.Type.TypeKind != TypeKind.TypeParameter), OpCodes.Callvirt.WithOperand(stringBuilderAppendMethod.MethodResolverExpression(context)), OpCodes.Pop ]); @@ -106,8 +106,8 @@ private void AddPrintMembersMethod(string stringBuilderAppendStringMethod, IName OpCodes.Pop, OpCodes.Ldarg_1, OpCodes.Ldarg_0, - OpCodes.Ldfld.WithOperand($"""new FieldReference("{field.Name}", {context.TypeResolver.ResolveAny(field.Type, ResolveTargetKind.TypeReference)}, {TypeOrClosedTypeFor(recordTypeDefinitionVariable)})"""), - OpCodes.Box.WithOperand(context.TypeResolver.ResolveAny(field.Type, ResolveTargetKind.Instruction).Expression).IgnoreIf(field.Type.TypeKind != TypeKind.TypeParameter), + OpCodes.Ldfld.WithOperand($"""new FieldReference("{field.Name}", {context.TypeResolver.Resolve(field.Type, ResolveTargetKind.TypeReference)}, {TypeOrClosedTypeFor(recordTypeDefinitionVariable)})"""), + OpCodes.Box.WithOperand(context.TypeResolver.Resolve(field.Type, ResolveTargetKind.Instruction).Expression).IgnoreIf(field.Type.TypeKind != TypeKind.TypeParameter), OpCodes.Callvirt.WithOperand(stringBuilderAppendMethod.MethodResolverExpression(context)), OpCodes.Pop ]); @@ -151,10 +151,10 @@ static IMethodSymbol AppendMethodFor(ITypeSymbol typeSymbol, ITypeSymbol type) private string PrintMembersMethodToCall(INamedTypeSymbol stringBuilderSymbol) { if (_recordSymbol is { IsGenericType: true }) - return $$"""new MethodReference("PrintMembers", {{context.TypeResolver.Bcl.System.Boolean}}, {{context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.None)}}) { HasThis = true, Parameters = { new ParameterDefinition({{context.TypeResolver.ResolveAny(stringBuilderSymbol, ResolveTargetKind.Parameter)}}) } }"""; + return $$"""new MethodReference("PrintMembers", {{context.TypeResolver.Bcl.System.Boolean}}, {{context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.None)}}) { HasThis = true, Parameters = { new ParameterDefinition({{context.TypeResolver.Resolve(stringBuilderSymbol, ResolveTargetKind.Parameter)}}) } }"""; return HasBaseRecord(context, _recordSymbol) - ? $$"""new MethodReference("PrintMembers", {{context.TypeResolver.Bcl.System.Boolean}}, {{context.TypeResolver.ResolveAny(_recordSymbol.BaseType, ResolveTargetKind.None)}}) { HasThis = true, Parameters = { new ParameterDefinition({{ context.TypeResolver.ResolveAny(stringBuilderSymbol, ResolveTargetKind.Parameter) }}) } }""" + ? $$"""new MethodReference("PrintMembers", {{context.TypeResolver.Bcl.System.Boolean}}, {{context.TypeResolver.Resolve(_recordSymbol.BaseType, ResolveTargetKind.None)}}) { HasThis = true, Parameters = { new ParameterDefinition({{ context.TypeResolver.Resolve(stringBuilderSymbol, ResolveTargetKind.Parameter) }}) } }""" : PrintMembersVar; } } diff --git a/Cecilifier.Core/CodeGeneration/Record.Generator.cs b/Cecilifier.Core/CodeGeneration/Record.Generator.cs index 395be538..3f793f28 100644 --- a/Cecilifier.Core/CodeGeneration/Record.Generator.cs +++ b/Cecilifier.Core/CodeGeneration/Record.Generator.cs @@ -79,10 +79,10 @@ private void AddCloneMethod() context, new BodiedMemberDefinitionContext( CloneMethodName, "Clone", cloneMethodVar, recordTypeDefinitionVariable, MemberOptions.None, IlContext.None), recordTypeDefinitionVariable, - Constants.Cecil.HideBySigNewSlotVirtual.AppendModifier("MethodAttributes.Public"), + Constants.Cecil.HideBySigNewSlotVirtual.AppendEnumFlag("MethodAttributes.Public"), [], [], - ctx => ctx.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.ReturnType), + ctx => ctx.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.ReturnType), out var _); context.Generate(clonedMethodExps); @@ -96,7 +96,7 @@ private void AddCloneMethod() InstructionRepresentation[] instructions = [ OpCodes.Ldarg_0, - OpCodes.Newobj.WithOperand(MethodOnClosedGenericTypeForMethodVariable(copyCtorVar.VariableName, recordTypeDefinitionVariable, context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Instruction)).AsToken()), + OpCodes.Newobj.WithOperand(MethodOnClosedGenericTypeForMethodVariable(copyCtorVar.VariableName, recordTypeDefinitionVariable, context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Instruction)).AsToken()), OpCodes.Ret ]; @@ -125,13 +125,13 @@ private void AddCopyConstructor() new BodiedMemberDefinitionContext("ctor", copyCtorVar, recordTypeDefinitionVariable, MemberOptions.None, IlContext.None), _recordSymbol.OriginalDefinition.ToDisplayString(), false, - Constants.Cecil.CtorAttributes.AppendModifier("MethodAttributes.Family | MethodAttributes.HideBySig"), + Constants.Cecil.CtorAttributes.AppendEnumFlag("MethodAttributes.Family | MethodAttributes.HideBySig"), new[] { _recordSymbol.ToDisplayString() }); context.Generate( [ ..exps, - ..CecilDefinitionsFactory.Parameter("other", RefKind.None, null, copyCtorVar, context.Naming.Parameter("other"), context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Parameter), Constants.ParameterAttributes.None, (null, false)) + ..CecilDefinitionsFactory.Parameter(context, "other", RefKind.None, null, copyCtorVar, context.Naming.Parameter("other"), context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Parameter), Constants.ParameterAttributes.None, (null, false)) ]); } else @@ -159,7 +159,7 @@ private void AddCopyConstructor() { var paramDefVar = context.DefinitionVariables.GetVariable(Utils.BackingFieldNameForAutoProperty(parameter.Identifier.ValueText()), VariableMemberKind.Field, _recordSymbol.OriginalDefinition.ToDisplayString()); var backingFieldRef = _recordSymbol is INamedTypeSymbol { IsGenericType: true } - ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Field)})" + ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Field)})" : paramDefVar.VariableName; instructions.Add(OpCodes.Ldarg_0); @@ -201,8 +201,8 @@ private void AddEqualityOperator() var equalsOperatorMethodVar = context.Naming.SyntheticVariable($"equalsOperator", ElementKind.Method); string declaringTypeName = _recordSymbol.OriginalDefinition.ToDisplayString(); IReadOnlyList parameters = [ - new ParameterSpec("left", context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?" }, - new ParameterSpec("right", context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?" } + new ParameterSpec("left", context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?" }, + new ParameterSpec("right", context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?" } ]; IList typeParameters = []; Func returnTypeResolver = ctx => ctx.TypeResolver.Bcl.System.Boolean; @@ -260,8 +260,8 @@ private void AddInequalityOperator() var inequalityOperatorMethodVar = context.Naming.SyntheticVariable($"inequalityOperator", ElementKind.Method); string declaringTypeName = recordSymbol.OriginalDefinition.ToDisplayString(); IReadOnlyList parameters = [ - new ParameterSpec("left", context.TypeResolver.ResolveAny(recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.OriginalDefinition.ToDisplayString()}?" }, - new ParameterSpec("right", context.TypeResolver.ResolveAny(recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.OriginalDefinition.ToDisplayString()}?" } + new ParameterSpec("left", context.TypeResolver.Resolve(recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.OriginalDefinition.ToDisplayString()}?" }, + new ParameterSpec("right", context.TypeResolver.Resolve(recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.OriginalDefinition.ToDisplayString()}?" } ]; IList typeParameters = []; Func returnTypeResolver = ctx => ctx.TypeResolver.Bcl.System.Boolean; @@ -277,14 +277,14 @@ private void AddInequalityOperator() context.Generate(inequalityOperatorMethodExps); - var equalityMethodDefinitionVariable = context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(_recordSymbol.OriginalDefinition.ToDisplayString(), "op_Equality", [$"{_recordSymbol.ToDisplayString()}?", $"{_recordSymbol.ToDisplayString()}?"], 0)).VariableName; - if (_recordSymbol is INamedTypeSymbol { IsGenericType: true }) + var equalityMethodDefinitionVariable = context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(_recordSymbol.OriginalDefinition.ToDisplayString(), "op_Equality", [$"{_recordSymbol.ToDisplayString()}?", $"{_recordSymbol.ToDisplayString()}?"], [])).VariableName; + if (_recordSymbol is { IsGenericType: true }) { var var = equalityMethodDefinitionVariable; equalityMethodDefinitionVariable = context.Naming.SyntheticVariable("equalsOperator", ElementKind.GenericInstance); context.Generate( [ - $"var {equalityMethodDefinitionVariable} = new MethodReference({var}.Name, {var}.ReturnType, {context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.TypeReference)}) {{ HasThis = {var}.HasThis, ExplicitThis = {var}.ExplicitThis, CallingConvention = {var}.CallingConvention, Parameters = {{ {var}.Parameters[0], {var}.Parameters[1] }} }};", + $"var {equalityMethodDefinitionVariable} = new MethodReference({var}.Name, {var}.ReturnType, {context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.TypeReference)}) {{ HasThis = {var}.HasThis, ExplicitThis = {var}.ExplicitThis, CallingConvention = {var}.CallingConvention, Parameters = {{ {var}.Parameters[0], {var}.Parameters[1] }} }};", ]); } @@ -340,7 +340,7 @@ private void AddGetHashCodeMethod() var equalityComparerMembersForParamType = _equalityComparerMembersCache[parameterType.Name]; var backingFieldRef = _recordSymbol is { IsGenericType: true } - ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.TypeReference)})" + ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.TypeReference)})" : paramDefVar.VariableName; getHashCodeMethodBodyExps.AddRange( @@ -387,7 +387,7 @@ void AddRecordClassHashCodeSpecificCode() if (HasBaseRecord(record)) { // Initialize the hashcode with 'base.GetHashCode() * -1521134295' - var baseGetHashCode = $$"""new MethodReference("GetHashCode", {{context.TypeResolver.Bcl.System.Int32}}, {{context.TypeResolver.ResolveAny(recordSymbol.BaseType, ResolveTargetKind.TypeReference)}}) { HasThis = true }"""; + var baseGetHashCode = $$"""new MethodReference("GetHashCode", {{context.TypeResolver.Bcl.System.Int32}}, {{context.TypeResolver.Resolve(recordSymbol.BaseType, ResolveTargetKind.TypeReference)}}) { HasThis = true }"""; getHashCodeMethodBodyExps.Add(OpCodes.Ldarg_0); getHashCodeMethodBodyExps.Add(OpCodes.Call.WithOperand(baseGetHashCode)); } @@ -451,7 +451,7 @@ void AddToStringMethod() using var _ = context.DefinitionVariables.WithVariable(methodDefinitionVariable); var stringBuildDefaultCtor = stringBuilderSymbol.GetMembers(".ctor").OfType().Single(ctor => ctor.Parameters.Length == 0).MethodResolverExpression(context); - ResolvedType[] localVariableTypes = [context.TypeResolver.ResolveAny(stringBuilderSymbol, ResolveTargetKind.LocalVariable)]; + ResolvedType[] localVariableTypes = [context.TypeResolver.Resolve(stringBuilderSymbol, ResolveTargetKind.LocalVariable)]; InstructionRepresentation[] instructions = [ OpCodes.Newobj.WithOperand(stringBuildDefaultCtor), OpCodes.Stloc_0, @@ -507,7 +507,7 @@ private string TypeOrClosedTypeFor(string recordVar) { if (_recordSymbol is { IsGenericType: true } genericRecord) { - var typeArguments = string.Join(',', genericRecord.TypeParameters.Select(tp => context.TypeResolver.ResolveAny(tp, ResolveTargetKind.TypeReference).Expression)); + var typeArguments = string.Join(',', genericRecord.TypeParameters.Select(tp => context.TypeResolver.Resolve(tp, ResolveTargetKind.TypeReference).Expression)); return $"{recordVar}.MakeGenericInstanceType([{typeArguments}])"; } @@ -525,10 +525,10 @@ private void AddIEquatableEquals() context.WriteNewLine(); context.WriteComment($"IEquatable<>.Equals({record.Identifier.ValueText} other)"); var equalsVar = context.Naming.SyntheticVariable("Equals", ElementKind.Method); - var declaringType = context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.TypeReference); + var declaringType = context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.TypeReference); string methodNameForParameterVariableRegistration = $"{_recordSymbol.OriginalDefinition.ToDisplayString()}.Equals"; - IReadOnlyList parameters = [new("other", context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?"} ]; + IReadOnlyList parameters = [new("other", context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{_recordSymbol.ToDisplayString()}?"} ]; var exps = context.ApiDefinitionsFactory.Method( context, new BodiedMemberDefinitionContext("Equals", methodNameForParameterVariableRegistration, equalsVar, recordTypeDefinitionVariable, MemberOptions.None, IlContext.None), @@ -559,7 +559,7 @@ private void AddIEquatableEquals() var paramDefVar = context.DefinitionVariables.GetVariable(Utils.BackingFieldNameForAutoProperty(parameter.Identifier.ValueText()), VariableMemberKind.Field, _recordSymbol.OriginalDefinition.ToDisplayString()); var backingFieldRef = _recordSymbol is { IsGenericType: true } - ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.ResolveAny(_recordSymbol, ResolveTargetKind.TypeReference)})" + ? $"new FieldReference({paramDefVar.VariableName}.Name, {paramDefVar.VariableName}.FieldType, {context.TypeResolver.Resolve(_recordSymbol, ResolveTargetKind.TypeReference)})" : paramDefVar.VariableName; // load property backing field for 'this' @@ -640,12 +640,11 @@ void CheckForReferenceEqualityIfApplicable(List instr foreach (var targetType in targetTypes) { - var openEqualityComparerType = context.TypeResolver.ResolveAny(context.RoslynTypeSystem.ForType(typeof(EqualityComparer<>).FullName), ResolveTargetKind.TypeReference); - //var openEqualityComparerType = context.TypeResolver.ResolveAny(context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(EqualityComparer<>).FullName!)); + var openEqualityComparerType = context.TypeResolver.Resolve(context.RoslynTypeSystem.ForType(typeof(EqualityComparer<>).FullName), ResolveTargetKind.TypeReference); if (equalityComparerDataByType.ContainsKey(targetType.Name)) continue; - var equalityComparerOfParameterType = openEqualityComparerType.MakeGenericInstanceType(context.TypeResolver.ResolveAny(targetType, ResolveTargetKind.TypeReference)); + var equalityComparerOfParameterType = openEqualityComparerType.MakeGenericInstanceType(context.TypeResolver.Resolve(targetType, ResolveTargetKind.TypeReference)); var openGetDefaultMethodVar = context.Naming.SyntheticVariable("openget_Default", ElementKind.LocalVariable); var getDefaultMethodVar = EmitDefaultPropertyGetterMethod(targetType, openGetDefaultMethodVar, equalityComparerOfParameterType); @@ -734,7 +733,7 @@ private void AddEqualityContractPropertyIfNeeded() propertyName, new Dictionary { ["get"] = "MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual" }, false, - resolveTargetKind => context.TypeResolver.ResolveAny(context.RoslynTypeSystem.SystemType, resolveTargetKind), + resolveTargetKind => context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemType, resolveTargetKind), string.Empty, // used for registering the parameter for setters. In this case, there are none. Array.Empty()); @@ -828,7 +827,7 @@ private void AddEqualsOverloads() context.WriteNewLine(); context.WriteComment($"Equals({recordSymbol.BaseType?.Name})"); var equalsBaseOverloadMethodVar = context.Naming.SyntheticVariable($"{methodName}{recordSymbol.BaseType?.Name}Overload", ElementKind.Method); - IReadOnlyList parameters1 = [new ParameterSpec("other", context.TypeResolver.ResolveAny(recordSymbol.BaseType, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{recordSymbol.BaseType?.Name}?" }]; + IReadOnlyList parameters1 = [new ParameterSpec("other", context.TypeResolver.Resolve(recordSymbol.BaseType, ResolveTargetKind.Parameter), RefKind.None, Constants.ParameterAttributes.None) { RegistrationTypeName = $"{recordSymbol.BaseType?.Name}?" }]; IList typeParameters1 = []; Func returnTypeResolver1 = ctx => ctx.TypeResolver.Bcl.System.Boolean; var equalsBaseOverloadMethodExps = context.ApiDefinitionsFactory.Method( @@ -869,7 +868,7 @@ private void AddDeconstructMethod() var parametersInfo = record.ParameterList!.Parameters.Select(p => (p.Identifier.ValueText, context.SemanticModel.GetTypeInfo(p.Type!).Type)); var parameterTypeParamSpec = parametersInfo.Select(parameterInfo => - new ParameterSpec(parameterInfo.ValueText, context.TypeResolver.ResolveAny(parameterInfo.Type, ResolveTargetKind.Parameter), RefKind.Out, Constants.ParameterAttributes.Out) { RegistrationTypeName = parameterInfo.Type.ToDisplayString()}) + new ParameterSpec(parameterInfo.ValueText, context.TypeResolver.Resolve(parameterInfo.Type, ResolveTargetKind.Parameter), RefKind.Out, Constants.ParameterAttributes.Out) { RegistrationTypeName = parameterInfo.Type.ToDisplayString()}) .ToArray(); context.WriteNewLine(); @@ -899,7 +898,7 @@ private void AddDeconstructMethod() deconstructInstructions.Add(OpCodes.Call.WithOperand(GetGetterMethodVar(recordSymbol, p.ValueText))); var stindOpCode = p.Type.StindOpCodeFor(); - deconstructInstructions.Add(stindOpCode == OpCodes.Stobj ? stindOpCode.WithOperand(context.TypeResolver.ResolveAny(p.Type, ResolveTargetKind.Instruction).Expression.AsToken()) : stindOpCode); + deconstructInstructions.Add(stindOpCode == OpCodes.Stobj ? stindOpCode.WithOperand(context.TypeResolver.Resolve(p.Type, ResolveTargetKind.Instruction).Expression.AsToken()) : stindOpCode); argIndex++; } deconstructInstructions.Add(OpCodes.Ret); @@ -913,13 +912,13 @@ private void AddDeconstructMethod() string GetGetterMethodVar(ITypeSymbol candidate, string propertyName) { - var getterMethodVar = context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(candidate.OriginalDefinition.ToDisplayString(), $"get_{propertyName}", [], 0)); + var getterMethodVar = context.DefinitionVariables.GetMethodVariable(new MethodDefinitionVariable(candidate.OriginalDefinition.ToDisplayString(), $"get_{propertyName}", [], [])); if (getterMethodVar.IsValid) { if (candidate is INamedTypeSymbol { IsGenericType: true }) { var var = getterMethodVar.VariableName; - return $"new MethodReference({var}.Name, {var}.ReturnType, {context.TypeResolver.ResolveAny(candidate, ResolveTargetKind.TypeReference)}) {{ HasThis = {var}.HasThis, ExplicitThis = {var}.ExplicitThis, CallingConvention = {var}.CallingConvention }}"; + return $"new MethodReference({var}.Name, {var}.ReturnType, {context.TypeResolver.Resolve(candidate, ResolveTargetKind.TypeReference)}) {{ HasThis = {var}.HasThis, ExplicitThis = {var}.ExplicitThis, CallingConvention = {var}.CallingConvention }}"; } return getterMethodVar.VariableName; diff --git a/Cecilifier.Core/Extensions/CecilifierContextExtensions.cs b/Cecilifier.Core/Extensions/CecilifierContextExtensions.cs index b52bd79d..6c7d7130 100644 --- a/Cecilifier.Core/Extensions/CecilifierContextExtensions.cs +++ b/Cecilifier.Core/Extensions/CecilifierContextExtensions.cs @@ -32,7 +32,7 @@ internal static DefinitionVariable AddLocalVariableToCurrentMethod(this IVisitor return context.ApiDefinitionsFactory.LocalVariable(context, localVarName, currentMethod.VariableName, varType); } - internal static bool TryApplyConversions(this IVisitorContext context, string ilVar, IOperation operation) + internal static bool TryApplyConversions(this IVisitorContext context, IlContext ilVar, IOperation operation) { if (operation is IConversionOperation { Conversion.IsNumeric: true } elementConversion) { @@ -46,11 +46,13 @@ internal static bool TryApplyConversions(this IVisitorContext context, string il } else if (operation is IConversionOperation { Operand.Type: not null } conversion2 && context.SemanticModel.Compilation.ClassifyConversion(conversion2.Operand.Type, operation.Type).IsBoxing) { - var resolutionContext = new TypeResolutionContext(ResolveTargetKind.Instruction, conversion2.Operand.Type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Box, context.TypeResolver.ResolveAny(conversion2.Operand.Type, resolutionContext).AsToken()); + var resolutionContext = new TypeResolutionContext(ResolveTargetKind.TypeReference, conversion2.Operand.Type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Box, context.TypeResolver.Resolve(conversion2.Operand.Type, resolutionContext).AsToken()); } else if (operation is IConversionOperation { Conversion.IsNullable: true } nullableConversion && !nullableConversion.Syntax.IsKind(SyntaxKind.CoalesceExpression)) { + //TODO: This code is very similar to the one handling coalescing operator below (before that code got fixed to handle SRM). Most likely we will want to extract some + // common code and reuse. context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Newobj, @@ -61,18 +63,30 @@ internal static bool TryApplyConversions(this IVisitorContext context, string il && SymbolEqualityComparer.Default.Equals(coalesce.Value.Type?.OriginalDefinition, context.RoslynTypeSystem.SystemNullableOfT) ) { - context.ApiDriver.WriteCilInstruction(context, - ilVar, + var targetType = context.TypeResolver.Resolve(coalesce.Type, ResolveTargetKind.GenericTypeArgument); + var closedNullable = context.TypeResolver.MakeGenericInstanceType(context.RoslynTypeSystem.SystemNullableOfT.NameIncludingTypeParametersAndArguments(), context.TypeResolver.Bcl.System.NullableOfT, [targetType], new TypeResolutionContext(ResolveTargetKind.Instruction, TypeResolutionOptions.IsValueType)); + var returnType = context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.ReturnType); + var resolvedCtor = context.MemberResolver.ResolveMethod("", closedNullable.Expression, ".ctor", returnType, [NullableTypeParameter(context)], [], MemberOptions.None); + + context.ApiDriver.WriteCilInstruction(context, + ilVar, OpCodes.Newobj, - $"assembly.MainModule.ImportReference(typeof(System.Nullable<>).MakeGenericType(typeof({coalesce.Type?.FullyQualifiedName()})).GetConstructors().Single(ctor => ctor.GetParameters().Length == 1))"); + resolvedCtor.AsToken()); } else return false; return true; + + static ParameterSpec NullableTypeParameter(IVisitorContext context) + { + var typeParameterSymbol = context.RoslynTypeSystem.SystemNullableOfT.TypeParameters[0]; + var resolvedNullableTypeParameter = context.TypeResolver.Resolve(typeParameterSymbol, ResolveTargetKind.TypeReference); + return new ParameterSpec(typeParameterSymbol.Name, resolvedNullableTypeParameter, RefKind.None, string.Empty); + } } - private static bool TryApplyNumericConversion(this IVisitorContext context, string ilVar, ITypeSymbol source, ITypeSymbol target) + private static bool TryApplyNumericConversion(this IVisitorContext context, IlContext ilVar, ITypeSymbol source, ITypeSymbol target) { if (source.SpecialType == target.SpecialType) return true; @@ -112,7 +126,7 @@ private static bool TryApplyNumericConversion(this IVisitorContext context, stri return true; } - internal static void AddCallToMethod(this IVisitorContext context, IMethodSymbol method, string ilVar, MethodDispatchInformation dispatchInformation = MethodDispatchInformation.MostLikelyVirtual) + internal static void AddCallToMethod(this IVisitorContext context, IMethodSymbol method, IlContext ilVar, MethodDispatchInformation dispatchInformation = MethodDispatchInformation.MostLikelyVirtual) { var needsVirtualDispatch = (method.IsVirtual || method.IsAbstract || method.IsOverride) && !method.ContainingType.IsPrimitiveType(); @@ -123,14 +137,21 @@ internal static void AddCallToMethod(this IVisitorContext context, IMethodSymbol EnsureForwardedMethod(context, method); - var operand = method.MethodResolverExpression(context); if (context.TryGetFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint, out var constrainedType)) { - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Constrained, constrainedType); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Constrained, constrainedType.AsToken()); context.ClearFlag(Constants.ContextFlags.MemberReferenceRequiresConstraint); } + var operand = GetMethodToInvoke(method).MethodResolverExpression(context); context.ApiDriver.WriteCilInstruction(context, ilVar, opCode, operand.AsToken()); + static IMethodSymbol GetMethodToInvoke(IMethodSymbol method) + { + if (method.ContainingType.IsValueType) + return method; + + return method.OverriddenMethod ?? method; + } } /* @@ -148,12 +169,9 @@ internal static void AddCallToMethod(this IVisitorContext context, IMethodSymbol */ public static void EnsureForwardedMethod(this IVisitorContext context, IMethodSymbol method) { - //TODO: The code of this method is causing problems when visiting methods in SRM; that driver - // will emit a method reference immediately and postpone the method definition to later - // and the check for retrieving the method variable bellow fails (because definition of the variable - // for the method definition has been postponed also). - // For now there are not tests relying on forwarded methods in SRM - if (Cecilifier.IsSRM) + // Some drivers do not require forward method references. For instance, SRM driver will emit a method reference + // when visiting a method definition and postpone the method definition emission. + if (!context.ApiDriver.DriverCapabilities.HasFlag(ApiDriverCapabilities.RequiresForwardReferences)) return; if (!method.IsDefinedInCurrentAssembly(context)) @@ -178,7 +196,7 @@ public static void EnsureForwardedMethod(this IVisitorContext context, IMethodSy : context.Naming.MethodDeclaration((BaseMethodDeclarationSyntax) method.DeclaringSyntaxReferences.SingleOrDefault()?.GetSyntax()); } - var resolvedReturnType = context.TypeResolver.ResolveAny(method.ReturnType, ResolveTargetKind.ReturnType); + var resolvedReturnType = context.TypeResolver.Resolve(method.ReturnType, ResolveTargetKind.ReturnType); var exps = context.ApiDefinitionsFactory.Method( context, new BodiedMemberDefinitionContext(methodName, methodNameForVariableRegistration,methodDeclarationVar, null, MemberOptions.None, IlContext.None), @@ -187,7 +205,7 @@ public static void EnsureForwardedMethod(this IVisitorContext context, IMethodSy method.Parameters.Select( p => new ParameterSymbolParameterSpec(p, context)).ToArray(), method.GetTypeParameterSyntax().Select(tps => tps.Identifier.Text).ToArray(), ctx => method.ReturnsByRef - ? resolvedReturnType.MakeByReferenceType() + ? context.TypeResolver.MakeByRefType(resolvedReturnType) : resolvedReturnType, out _); context.Generate(exps); diff --git a/Cecilifier.Core/Extensions/CecilifierExtensions.cs b/Cecilifier.Core/Extensions/CecilifierExtensions.cs index 4e880576..af823163 100644 --- a/Cecilifier.Core/Extensions/CecilifierExtensions.cs +++ b/Cecilifier.Core/Extensions/CecilifierExtensions.cs @@ -36,20 +36,20 @@ public static string PascalCase(this string str) }); } - public static string AppendModifier(this string to, string modifier) + public static string AppendEnumFlag(this string to, string flag) { - if (string.IsNullOrWhiteSpace(modifier)) + if (string.IsNullOrWhiteSpace(flag)) return to; if (string.IsNullOrEmpty(to)) - return modifier; + return flag; - return $"{to} | {modifier}"; + return $"{to} | {flag}"; } - public static StringBuilder AppendModifier(this StringBuilder to, string modifier) + public static StringBuilder AppendEnumFlag(this StringBuilder to, string flag) { - if (string.IsNullOrWhiteSpace(modifier)) + if (string.IsNullOrWhiteSpace(flag)) { return to; } @@ -58,9 +58,28 @@ public static StringBuilder AppendModifier(this StringBuilder to, string modifie { to.Append(" | "); } + + return to.Append(flag); + } + + public static Span AppendEnumFlag(this Span to, ReadOnlySpan flag, bool isFirst = true) + { + if (flag.Length == 0) + { + return to; + } + + if (!isFirst) + { + ReadOnlySpan span = " | ".AsSpan(); + ArgumentOutOfRangeException.ThrowIfGreaterThan(span.Length, to.Length); + span.CopyTo(to); + to = to.Slice(span.Length); + } - to.Append(modifier); - return to; + ArgumentOutOfRangeException.ThrowIfGreaterThan(flag.Length, to.Length); + flag.CopyTo(to); + return to.Slice(flag.Length); } public static T ResolveDeclaringType(this SyntaxNode node) where T : BaseTypeDeclarationSyntax diff --git a/Cecilifier.Core/Extensions/DefinitionVariableExtensions.cs b/Cecilifier.Core/Extensions/DefinitionVariableExtensions.cs index 7551d882..5ee87750 100644 --- a/Cecilifier.Core/Extensions/DefinitionVariableExtensions.cs +++ b/Cecilifier.Core/Extensions/DefinitionVariableExtensions.cs @@ -7,10 +7,10 @@ namespace Cecilifier.Core.Extensions; public static class DefinitionVariableExtensions { [ExcludeFromCodeCoverage] - public static DefinitionVariable ThrowIfVariableIsNotValid(this DefinitionVariable variable) + public static DefinitionVariable ThrowIfVariableIsNotValid(this DefinitionVariable variable, string description = "") { if (!variable.IsValid) - throw new Exception($"Could not resolve variable definition for {variable.MemberName} ({variable.Kind}, Parent: {variable.ParentName ?? "N/A"})"); + throw new Exception($"Could not resolve variable definition for {variable.MemberName} ({variable.Kind}, Parent: {variable.ParentName ?? "N/A"}): {description ?? "no description provided"}"); return variable; } diff --git a/Cecilifier.Core/Extensions/ExpressionExtensions.cs b/Cecilifier.Core/Extensions/ExpressionExtensions.cs index 5a8c4d19..c682981a 100644 --- a/Cecilifier.Core/Extensions/ExpressionExtensions.cs +++ b/Cecilifier.Core/Extensions/ExpressionExtensions.cs @@ -33,7 +33,7 @@ internal static string ValueText(this LiteralExpressionSyntax node) throw new ArgumentException($"{node.Kind()} is not supported."); } - public static void InjectRequiredConversions(this ExpressionSyntax expression, IVisitorContext context, string ilVar, Action loadArrayIntoStack = null) + public static void InjectRequiredConversions(this ExpressionSyntax expression, IVisitorContext context, IlContext ilVar, Action loadArrayIntoStack = null) { var operation = context.SemanticModel.GetOperation(expression); if (SymbolEqualityComparer.Default.Equals(operation?.Type, context.RoslynTypeSystem.SystemIndex) && !expression.IsKind(SyntaxKind.IndexExpression) && loadArrayIntoStack != null) @@ -59,7 +59,7 @@ public static void InjectRequiredConversions(this ExpressionSyntax expression, I var conversion = context.SemanticModel.GetConversion(expression); if (conversion.IsImplicit && NeedsBoxing(context, expression, typeInfo.Type)) { - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Box, context.TypeResolver.ResolveAny(typeInfo.Type, ResolveTargetKind.TypeReference)); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Box, context.TypeResolver.Resolve(typeInfo.Type, ResolveTargetKind.TypeReference)); } } diff --git a/Cecilifier.Core/Extensions/ISymbolExtensions.cs b/Cecilifier.Core/Extensions/ISymbolExtensions.cs index 2c14edb4..4c0db5fd 100644 --- a/Cecilifier.Core/Extensions/ISymbolExtensions.cs +++ b/Cecilifier.Core/Extensions/ISymbolExtensions.cs @@ -13,10 +13,13 @@ using Cecilifier.Core.AST.Params; using Cecilifier.Core.Misc; using Cecilifier.Core.Naming; +using Cecilifier.Core.TypeSystem; using Cecilifier.Core.Variables; namespace Cecilifier.Core.Extensions { + public record LoadAddressDetails(OpCode OpCode, Func Factory); + public static class ISymbolExtensions { private static readonly SymbolDisplayFormat QualifiedNameWithoutTypeParametersFormat = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces) @@ -74,9 +77,32 @@ public static string ToValidVariableName(this ISymbol symbol) }; } + public static string NameIncludingTypeParametersAndArguments(this ISymbol symbol) + { + return symbol switch + { + INamedTypeSymbol {TypeArguments.Length: > 0 } namedTypeSymbol => $"{namedTypeSymbol.Name}_{namedTypeSymbol.TypeArguments.Aggregate(new StringBuilder(), MemberNameIncludingContainingType)}", + INamedTypeSymbol {TypeParameters.Length: > 0 } namedTypeSymbol => $"{namedTypeSymbol.Name}_{namedTypeSymbol.TypeParameters.Aggregate(new StringBuilder(), MemberNameIncludingContainingType)}", + INamedTypeSymbol namedTypeSymbol => $"{namedTypeSymbol.Name}", + + _ => throw new NotSupportedException($"Symbol {symbol.ToDisplayString()} is not supported.") + }; + + // In some scenarios we may want/need to register variables for types and take their containing type symbol. + StringBuilder MemberNameIncludingContainingType(StringBuilder acc, ITypeSymbol type) + { + HashCode hashCode = new(); + hashCode.Add(type.Name); + hashCode.Add(type.ContainingSymbol?.Name); + acc.Append($"{hashCode.ToHashCode()}"); + + return acc; + } + } + public static string GetReflectionName(this ITypeSymbol typeSymbol) { - var sb = new System.Text.StringBuilder(); + var sb = new StringBuilder(); if (typeSymbol is IArrayTypeSymbol array) { @@ -136,7 +162,7 @@ public static bool IsByRef(this ISymbol symbol) => [ExcludeFromCodeCoverage] [return: NotNull] - public static T EnsureNotNull([NotNullIfNotNull("symbol")] this T? symbol, [CallerArgumentExpression(nameof(symbol))] string expression = null) where T : ISymbol + public static T EnsureNotNull([NotNullIfNotNull("symbol")] this T? symbol, [CallerArgumentExpression(nameof(symbol))] string? expression = null) where T : ISymbol { if (symbol == null) throw new NullReferenceException($"Expression '{expression}' is expected to be non null."); @@ -151,7 +177,7 @@ public static string AsParameterAttribute(this IParameterSymbol symbol) if (string.IsNullOrWhiteSpace(refRelatedAttr) && string.IsNullOrWhiteSpace(optionalAttribute)) return Constants.ParameterAttributes.None; - return refRelatedAttr.AppendModifier(optionalAttribute); + return refRelatedAttr.AppendEnumFlag(optionalAttribute); } public static (string Value, bool Present) ExplicitDefaultValue(this IParameterSymbol symbol, bool rawString = true) @@ -183,7 +209,7 @@ public static void EnsureFieldExists(this ISymbol fieldSymbol, IVisitorContext c if (declaringSyntaxReference == null) return; - var fieldDeclaration = declaringSyntaxReference.GetSyntax().Parent.Parent.EnsureNotNull(); + var fieldDeclaration = declaringSyntaxReference.GetSyntax().Parent.Parent.EnsureNotNull(); if (fieldDeclaration.Span.Start > node.Span.End) { // this is a forward reference, process it... @@ -213,14 +239,17 @@ public static void EnsurePropertyExists(this IPropertySymbol propertySymbol, IVi public static OpCode LoadOpCodeForFieldAccess(this ISymbol symbol) => symbol.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld; public static OpCode StoreOpCodeForFieldAccess(this ISymbol symbol) => symbol.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld; - public static OpCode LoadAddressOpcodeForMember(this ISymbol symbol) => symbol.Kind switch + public static LoadAddressDetails LoadAddressDetailsForForMember(this ISymbol symbol) => symbol.Kind switch { - SymbolKind.Field => symbol.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, - SymbolKind.Parameter => OpCodes.Ldarg_S, - SymbolKind.Local => OpCodes.Ldloca_S, + SymbolKind.Field => new LoadAddressDetails(symbol.IsStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, AsToken), + SymbolKind.Parameter => new LoadAddressDetails(OpCodes.Ldarg_S, AsToken), + SymbolKind.Local => new LoadAddressDetails(OpCodes.Ldloca_S, AsLocalVariable), _ => throw new ArgumentException($"Invalid symbol type for {symbol} ({symbol.Kind})") }; + static object AsLocalVariable(string expression) => expression.AsLocalVariable(); + static object AsToken(string expression) => expression.AsToken(); + public static OpCode LoadOpCodeFor(this ITypeSymbol type) { return type.SpecialType switch @@ -296,8 +325,10 @@ public static bool HasTypeArgumentOfTypeFromCecilifiedCodeTransitive(this INamed return type.TypeArguments.Any(t => t.IsDefinedInCurrentAssembly(context)) || (type.ContainingType != null && (SymbolEqualityComparer.Default.Equals(type.ContainingType, type) ? false : HasTypeArgumentOfTypeFromCecilifiedCodeTransitive(type.ContainingType, context))); } + + public static TypeResolutionOptions GetTypeResolutionOptions(this ITypeSymbol type) => type.IsValueType ? TypeResolutionOptions.IsValueType : TypeResolutionOptions.None; - internal static ExpandedParamsArgumentHandler? CreateExpandedParamsUsageHandler(this IMethodSymbol methodSymbol, ExpressionVisitor expressionVisitor, string ilVar, ArgumentListSyntax argumentList) + internal static ExpandedParamsArgumentHandler? CreateExpandedParamsUsageHandler(this IMethodSymbol methodSymbol, ExpressionVisitor expressionVisitor, IlContext ilVar, ArgumentListSyntax argumentList) { var paramsParameter = methodSymbol.Parameters.FirstOrDefault(p => p.IsParams); if (paramsParameter == null || !IsExpandedForm(argumentList, paramsParameter)) @@ -367,13 +398,13 @@ internal static string MethodsModifier(this IMethodSymbol methodSymbol) var modifiers = new StringBuilder(methodModifiers); if (methodSymbol.IsStatic) - modifiers.AppendModifier($"{methodAttributesEnumName}.Static"); + modifiers.AppendEnumFlag($"{methodAttributesEnumName}.Static"); if (methodSymbol.IsAbstract) - modifiers.AppendModifier($"{methodAttributesEnumName}.Abstract"); + modifiers.AppendEnumFlag($"{methodAttributesEnumName}.Abstract"); if (methodSymbol.IsVirtual) - modifiers.AppendModifier($"{methodAttributesEnumName}.Virtual"); + modifiers.AppendEnumFlag($"{methodAttributesEnumName}.Virtual"); return modifiers.ToString(); } diff --git a/Cecilifier.Core/Extensions/MethodExtensions.cs b/Cecilifier.Core/Extensions/MethodExtensions.cs index f183732c..bfca6e4f 100644 --- a/Cecilifier.Core/Extensions/MethodExtensions.cs +++ b/Cecilifier.Core/Extensions/MethodExtensions.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using MethodAttributes=Mono.Cecil.MethodAttributes; namespace Cecilifier.Core.Extensions { @@ -62,11 +61,11 @@ public static MethodDefinitionVariable AsRawMethodDefinitionVariable(this IMetho method.ContainingType.ToDisplayString(), method.Name, method.Parameters.Select(p => p.Type.ToDisplayString()).ToArray(), - method.TypeParameters.Length, + method.TypeParameters.Select(tp => tp.Name).ToArray(), variableName); } - public static string MethodModifiersToCecil(this IEnumerable modifiers, string specificModifiers = null, IMethodSymbol methodSymbol = null) + public static string MethodModifiersToCecil(this IEnumerable modifiers, IVisitorContext context, string specificModifiers = null, IMethodSymbol methodSymbol = null) { var lastDeclaredIn = methodSymbol.FindLastDefinition(); var modifiersStr = MapExplicitModifiers(modifiers, lastDeclaredIn.ContainingType.TypeKind); @@ -74,13 +73,15 @@ public static string MethodModifiersToCecil(this IEnumerable modifi var defaultAccessibility = lastDeclaredIn.ContainingType.TypeKind == TypeKind.Interface ? "Public" : "Private"; if (modifiersStr == string.Empty && methodSymbol != null) { + modifiersStr = modifiersStr.AppendEnumFlag(context.MemberResolver.MapSpecificAttributes(methodSymbol)); + if (methodSymbol.IsExplicitMethodImplementation()) { - modifiersStr = Constants.Cecil.InterfaceMethodDefinitionAttributes.AppendModifier("MethodAttributes.Final"); + modifiersStr = Constants.Cecil.InterfaceMethodDefinitionAttributes.AppendEnumFlag("MethodAttributes.Final"); } else if (lastDeclaredIn.ContainingType.TypeKind == TypeKind.Interface && !methodSymbol.IsStatic) { - modifiersStr = Constants.Cecil.InterfaceMethodDefinitionAttributes.AppendModifier( + modifiersStr = Constants.Cecil.InterfaceMethodDefinitionAttributes.AppendEnumFlag( SymbolEqualityComparer.Default.Equals(lastDeclaredIn.ContainingType, methodSymbol.ContainingType) ? "MethodAttributes.Abstract" : "MethodAttributes.Final"); @@ -93,17 +94,18 @@ public static string MethodModifiersToCecil(this IEnumerable modifi var validModifiers = RemoveSourceModifiersWithNoILEquivalent(modifiers); - var cecilModifiersStr = new StringBuilder(SyntaxWalkerBase.ModifiersToCecil(validModifiers.ToList(), defaultAccessibility, MapMethodAttributeFor)); + var cecilModifiersStr = new StringBuilder(SyntaxWalkerBase.ModifiersAsString(validModifiers.ToList(), defaultAccessibility, MapMethodAttributeFor)); if (specificModifiers != null) { - cecilModifiersStr.AppendModifier(specificModifiers); + cecilModifiersStr.AppendEnumFlag(specificModifiers); } - cecilModifiersStr.AppendModifier("MethodAttributes.HideBySig").AppendModifier(modifiersStr); + cecilModifiersStr.AppendEnumFlag("MethodAttributes.HideBySig").AppendEnumFlag(modifiersStr); if (methodSymbol.HasCovariantReturnType()) { - cecilModifiersStr.AppendModifier("MethodAttributes.NewSlot"); + cecilModifiersStr.AppendEnumFlag("MethodAttributes.NewSlot"); } + return cecilModifiersStr.ToString(); } @@ -114,12 +116,12 @@ public static string ModifiersForSyntheticMethod(this SyntaxTokenList modifiers, var validModifiers = RemoveSourceModifiersWithNoILEquivalent(modifiers); - var cecilModifiersStr = new StringBuilder(SyntaxWalkerBase.ModifiersToCecil(validModifiers.ToList(), defaultAccessibility, MapMethodAttributeFor)); - cecilModifiersStr.AppendModifier(specificModifiers); - cecilModifiersStr.AppendModifier("MethodAttributes.HideBySig").AppendModifier(modifiersStr); + var cecilModifiersStr = new StringBuilder(SyntaxWalkerBase.ModifiersAsString(validModifiers.ToList(), defaultAccessibility, MapMethodAttributeFor)); + cecilModifiersStr.AppendEnumFlag(specificModifiers); + cecilModifiersStr.AppendEnumFlag("MethodAttributes.HideBySig").AppendEnumFlag(modifiersStr); if (declaringType.TypeKind == TypeKind.Interface) - cecilModifiersStr.AppendModifier(Constants.Cecil.InterfaceMethodDefinitionAttributes).AppendModifier("MethodAttributes.Abstract"); + cecilModifiersStr.AppendEnumFlag(Constants.Cecil.InterfaceMethodDefinitionAttributes).AppendEnumFlag("MethodAttributes.Abstract"); return cecilModifiersStr.ToString(); } @@ -133,13 +135,13 @@ public static IEnumerable MakeGenericInstanceMethod(this string methodRe hash.Add(resolvedTypeArguments.Count); foreach (var t in resolvedTypeArguments) hash.Add(t); - + List exps = new(); varName = context.Services.Get>().GetOrCreate(hash.ToHashCode(), (context, methodName, resolvedTypeArguments, methodReferenceVariable, exps), static (hashCode, state) => { var genericInstanceVarName = state.context.Naming.SyntheticVariable(state.methodName, ElementKind.GenericInstance); - + state.exps.Add($"var {genericInstanceVarName} = new GenericInstanceMethod({state.methodReferenceVariable});"); foreach (var t in state.resolvedTypeArguments) { @@ -147,24 +149,16 @@ public static IEnumerable MakeGenericInstanceMethod(this string methodRe } return genericInstanceVarName; }); - + return exps; } - - public static string MakeGenericInstanceMethod(this string methodReferenceVariable, IVisitorContext context, string methodName, IReadOnlyList resolvedTypeArguments) - { - var exps = methodReferenceVariable.MakeGenericInstanceMethod(context, methodName, resolvedTypeArguments, out var genericInstanceVarName); - context.Generate(exps); - return genericInstanceVarName; - } - public static string MakeGenericInstanceMethod(this string methodReferenceVariable, IVisitorContext context, IMethodSymbol method) { if (method.IsGenericMethod is false) return methodReferenceVariable; - var exps = methodReferenceVariable.MakeGenericInstanceMethod(context, method.Name, method.TypeArguments.Select(t => context.TypeResolver.ResolveAny(t, ResolveTargetKind.TypeReference)).ToList(), out var genericInstanceVarName); + var exps = methodReferenceVariable.MakeGenericInstanceMethod(context, method.Name, method.TypeArguments.Select(t => context.TypeResolver.Resolve(t, ResolveTargetKind.TypeReference)).ToList(), out var genericInstanceVarName); context.Generate(exps); return genericInstanceVarName; @@ -186,7 +180,7 @@ private static string MapExplicitModifiers(IEnumerable modifiers, T case SyntaxKind.OverrideKeyword: return "MethodAttributes.Virtual"; case SyntaxKind.AbstractKeyword: - return "MethodAttributes.Virtual | MethodAttributes.Abstract".AppendModifier(typeKind != TypeKind.Interface || !modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)) ? "MethodAttributes.NewSlot" : string.Empty); + return "MethodAttributes.Virtual | MethodAttributes.Abstract".AppendEnumFlag(typeKind != TypeKind.Interface || !modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)) ? "MethodAttributes.NewSlot" : string.Empty); case SyntaxKind.SealedKeyword: return "MethodAttributes.Final"; case SyntaxKind.NewKeyword: diff --git a/Cecilifier.Core/Extensions/StringExtensions.cs b/Cecilifier.Core/Extensions/StringExtensions.cs index a14aa6d5..4f877b4d 100644 --- a/Cecilifier.Core/Extensions/StringExtensions.cs +++ b/Cecilifier.Core/Extensions/StringExtensions.cs @@ -17,5 +17,16 @@ public static class StringExtensions public static CilLocalVariableHandle AsLocalVariable(this string value) => new(value); public static CilToken AsToken(this string value) => new(value); public static CilToken AsToken(this ResolvedType value) => new(value.Expression); + + public static int ComputeHashCode(this string[] values) + { + if (values == null) return 0; + + var hash = new HashCode(); + foreach (var v in values) + hash.Add(v); + + return hash.ToHashCode(); + } } } diff --git a/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs b/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs index e57b6843..0e7175e7 100644 --- a/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs +++ b/Cecilifier.Core/Extensions/SyntaxNodeExtensions.cs @@ -130,6 +130,12 @@ public static TTarget EnsureNotNull([NotNullIfNotNull("source" return (TTarget) source; } + + public static T EnsureNotNull([NotNullIfNotNull(nameof(node))] this T? node, [CallerArgumentExpression("node")] string? msg = null) where T : SyntaxNode + { + return node.EnsureNotNull(msg); + } + #nullable restore internal static bool IsPassedAsInParameter(this ArgumentSyntax toBeChecked, IVisitorContext context) @@ -205,7 +211,7 @@ internal static IEnumerable ToCustomAttributeArguments( Name= argument.NameEquals!.Name.Identifier.Text, Value = constantValue.Value, Kind = namedArgumentSymbol!.Kind == SymbolKind.Field ? NamedArgumentKind.Field : NamedArgumentKind.Property, - ResolvedType = context.TypeResolver.ResolveAny(namedArgumentSymbol.GetMemberType(), ResolveTargetKind.AttributeNamedArgument) + ResolvedType = context.TypeResolver.Resolve(namedArgumentSymbol.GetMemberType(), ResolveTargetKind.AttributeNamedArgument) }; } } @@ -219,7 +225,7 @@ private static Optional GetConstantValue(IVisitorContext context, Expres { return expression switch { - TypeOfExpressionSyntax typeOf => new RawCSharpCode(context.TypeResolver.ResolveAny(context.SemanticModel.GetTypeInfo(typeOf.Type).Type, ResolveTargetKind.TypeReference).Expression), + TypeOfExpressionSyntax typeOf => new RawCSharpCode(context.TypeResolver.Resolve(context.SemanticModel.GetTypeInfo(typeOf.Type).Type, ResolveTargetKind.TypeReference).Expression), ImplicitArrayCreationExpressionSyntax implicitArrayCreation => ConstantValueForArray(implicitArrayCreation.Initializer), ArrayCreationExpressionSyntax arrayCreation => ConstantValueForArray(arrayCreation.Initializer), diff --git a/Cecilifier.Core/Extensions/TypeExtensions.cs b/Cecilifier.Core/Extensions/TypeExtensions.cs index c2e25cae..51ffb1a7 100644 --- a/Cecilifier.Core/Extensions/TypeExtensions.cs +++ b/Cecilifier.Core/Extensions/TypeExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.Handles; using Microsoft.CodeAnalysis; using Cecilifier.Core.AST; @@ -16,11 +15,6 @@ public static class TypeExtensions public static bool IsNonPrimitiveValueType(this ITypeSymbol type, IVisitorContext context) => !type.IsPrimitiveType() && (type.IsValueType || SymbolEqualityComparer.Default.Equals(type, context.RoslynTypeSystem.SystemValueType)); - public static ResolvedType MakeByReferenceType(this ResolvedType type) - { - return $"{type}.MakeByReferenceType()"; - } - public static ResolvedType MakeGenericInstanceType(this ResolvedType type, IEnumerable typeArguments) { return new ResolvedType($"{type}.MakeGenericInstanceType({string.Join(", ", typeArguments)})"); @@ -47,6 +41,24 @@ public static ResolvedType MakeGenericInstanceType(this ResolvedType type, Resol SpecialType.System_UInt64 => true, _ => false }; + + public static bool IsConsideredPreDefinedType(this ITypeSymbol type) + { + return !(type.SpecialType == SpecialType.None + || type.SpecialType == SpecialType.System_Array + || type.SpecialType == SpecialType.System_Enum + || type.SpecialType == SpecialType.System_ValueType + || type.SpecialType == SpecialType.System_Decimal + || type.SpecialType == SpecialType.System_DateTime + || type.SpecialType == SpecialType.System_Delegate + || type.SpecialType == SpecialType.System_MulticastDelegate + || type.SpecialType == SpecialType.System_AsyncCallback + || type.SpecialType == SpecialType.System_RuntimeTypeHandle + || type.SpecialType == SpecialType.System_RuntimeFieldHandle + || type.SpecialType == SpecialType.System_Runtime_CompilerServices_IsVolatile + || type.SpecialType == SpecialType.System_Nullable_T + || type.TypeKind == TypeKind.Interface); + } public static ITypeSymbol ElementTypeSymbolOf(this ITypeSymbol type) => type switch { diff --git a/Cecilifier.Core/Misc/CecilDefinitionsFactory.cs b/Cecilifier.Core/Misc/CecilDefinitionsFactory.cs index 87610e98..53f40453 100644 --- a/Cecilifier.Core/Misc/CecilDefinitionsFactory.cs +++ b/Cecilifier.Core/Misc/CecilDefinitionsFactory.cs @@ -40,22 +40,22 @@ public static string GenericParameter(IVisitorContext context, string ownerConta return $"var {genParamDefVar} = new Mono.Cecil.GenericParameter(\"{genericParamName}\", {typeParameterOwnerVar});"; } - public static string ParameterDoesNotHandleParamsKeywordOrDefaultValue(string name, RefKind byRef, ResolvedType resolvedType, string? paramAttributes = null) + public static string ParameterDoesNotHandleParamsKeywordOrDefaultValue(ITypeResolver resolver, string name, RefKind byRef, ResolvedType resolvedType, string? paramAttributes = null) { paramAttributes ??= Constants.ParameterAttributes.None; if (RefKind.None != byRef) { - resolvedType = resolvedType.MakeByReferenceType(); + resolvedType = resolver.MakeByRefType(resolvedType); } return $"new ParameterDefinition(\"{name}\", {paramAttributes}, {resolvedType})"; } - public static IEnumerable Parameter(string name, RefKind byRef, string? paramsAttributeTypeName, string methodVar, string paramVar, ResolvedType resolvedType, string paramAttributes, (string? Value, bool Present) defaultParameterValue) + public static IEnumerable Parameter(IVisitorContext ctx, string name, RefKind byRef, string? paramsAttributeTypeName, string methodVar, string paramVar, ResolvedType resolvedType, string paramAttributes, (string? Value, bool Present) defaultParameterValue) { var exps = new List(); - exps.Add($"var {paramVar} = {ParameterDoesNotHandleParamsKeywordOrDefaultValue(name, byRef, resolvedType, paramAttributes)};"); + exps.Add($"var {paramVar} = {ParameterDoesNotHandleParamsKeywordOrDefaultValue(ctx.TypeResolver, name, byRef, resolvedType, paramAttributes)};"); if (!string.IsNullOrWhiteSpace(paramsAttributeTypeName)) { exps.Add($"{paramVar}.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(typeof({paramsAttributeTypeName}).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null))));"); @@ -79,12 +79,13 @@ public static IEnumerable Parameter(IVisitorContext context, ParameterSy public static IEnumerable Parameter(IVisitorContext context, IParameterSymbol paramSymbol, string methodVar, string paramVar) { return Parameter( + context, paramSymbol.Name, paramSymbol.RefKind, paramSymbol.ParamsAttributeMatchingType(), methodVar, paramVar, - context.TypeResolver.ResolveAny(paramSymbol.Type, ResolveTargetKind.Parameter.ToTypeResolutionContext(methodVar)), + context.TypeResolver.Resolve(paramSymbol.Type, ResolveTargetKind.Parameter.ToTypeResolutionContext(methodVar)), paramSymbol.AsParameterAttribute(), paramSymbol.ExplicitDefaultValue(rawString: false)); } @@ -105,53 +106,11 @@ public static string DefaultTypeAttributeFor(TypeKind typeKind, bool hasStaticCt private static string FunctionPointerTypeBasedCecilType(ITypeResolver resolver, IFunctionPointerTypeSymbol functionPointer, Func factory) { - var parameters = $"Parameters={{ {string.Join(',', functionPointer.Signature.Parameters.Select(p => ParameterDoesNotHandleParamsKeywordOrDefaultValue(p.Name, p.RefKind, resolver.ResolveAny(p.Type, ResolveTargetKind.Parameter))))} }}"; - var returnType = resolver.ResolveAny(functionPointer.Signature.ReturnType, ResolveTargetKind.ReturnType); + var parameters = $"Parameters={{ {string.Join(',', functionPointer.Signature.Parameters.Select(p => ParameterDoesNotHandleParamsKeywordOrDefaultValue(resolver, p.Name, p.RefKind, resolver.Resolve(p.Type, ResolveTargetKind.Parameter))))} }}"; + var returnType = resolver.Resolve(functionPointer.Signature.ReturnType, ResolveTargetKind.ReturnType); return factory("HasThis = false", parameters, returnType); } - public static void InstantiateDelegate(IVisitorContext context, string ilVar, ITypeSymbol delegateType, string targetMethodExp, StaticDelegateCacheContext staticDelegateCacheContext) - { - // To match Roslyn implementation we need to cache static method do delegate conversions. - if (staticDelegateCacheContext.IsStaticDelegate) - { - staticDelegateCacheContext.EnsureCacheBackingFieldIsEmitted(context.TypeResolver.ResolveAny(delegateType, ResolveTargetKind.TypeReference)); - LogWarningIfStaticMethodIsDeclaredInOtherType(context, staticDelegateCacheContext); - - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldsfld, staticDelegateCacheContext.CacheBackingField); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Dup); - - var cacheAlreadyInitializedTargetVarName = context.Naming.Label("cacheHit"); - context.Generate($"var {cacheAlreadyInitializedTargetVarName} = {ilVar}.Create(OpCodes.Nop);"); - context.WriteNewLine(); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Brtrue, cacheAlreadyInitializedTargetVarName); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Pop); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldnull); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldftn, targetMethodExp); - var delegateCtor = delegateType.GetMembers().OfType().FirstOrDefault(m => m.Name == ".ctor"); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Newobj, delegateCtor.MethodResolverExpression(context)); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Dup); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Stsfld, staticDelegateCacheContext.CacheBackingField); - context.Generate($"{ilVar}.Append({cacheAlreadyInitializedTargetVarName});"); - context.WriteNewLine(); - } - else - { - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldftn, targetMethodExp); - var delegateCtor = delegateType.GetMembers().OfType().FirstOrDefault(m => m.Name == ".ctor"); - context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Newobj, delegateCtor.MethodResolverExpression(context)); - } - } - - private static void LogWarningIfStaticMethodIsDeclaredInOtherType(IVisitorContext context, StaticDelegateCacheContext staticDelegateCacheContext) - { - var currentType = context.DefinitionVariables.GetLastOf(VariableMemberKind.Type); - if (currentType.IsValid && currentType.MemberName != staticDelegateCacheContext.Method.ContainingType.Name) - { - context.EmitWarning($"Converting static method ({staticDelegateCacheContext.Method.FullyQualifiedName()}) to delegate in a type other than the one defining it may generate incorrect code. Access type: {currentType.MemberName}, Method type: {staticDelegateCacheContext.Method.ContainingType.Name}"); - } - } - public static class Collections { /// @@ -164,9 +123,9 @@ public static class Collections /// /// for the List{T}. /// Number of elements to be stored. - public static (DefinitionVariable, ResolvedType) InstantiateListToStoreElements(IVisitorContext context, string ilVar, INamedTypeSymbol listOfTTypeSymbol, int elementCount) + public static (DefinitionVariable, ResolvedType) InstantiateListToStoreElements(IVisitorContext context, IlContext ilVar, INamedTypeSymbol listOfTTypeSymbol, int elementCount) { - var resolvedListTypeArgument = context.TypeResolver.ResolveAny(listOfTTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference); + var resolvedListTypeArgument = context.TypeResolver.Resolve(listOfTTypeSymbol.TypeArguments[0], ResolveTargetKind.TypeReference); context.WriteNewLine(); context.WriteComment("Instantiates a List passing the # of elements to its ctor."); @@ -180,7 +139,7 @@ public static (DefinitionVariable, ResolvedType) InstantiateListToStoreElements( // Calls 'CollectionsMarshal.SetCount(list, num)' on the list. var collectionMarshalTypeSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(System.Runtime.InteropServices.CollectionsMarshal).FullName!).EnsureNotNull(); - var setCountMethod = collectionMarshalTypeSymbol.GetMembers("SetCount").OfType().Single().MethodResolverExpression(context).MakeGenericInstanceMethod(context, "SetCount", [ resolvedListTypeArgument ]); + var setCountMethod = context.MemberResolver.MakeGeneticInstanceMethod(collectionMarshalTypeSymbol.GetMembers("SetCount").OfType().Single().MethodResolverExpression(context), "SetCount", [ resolvedListTypeArgument ]); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Dup); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldc_I4, elementCount); @@ -190,11 +149,11 @@ public static (DefinitionVariable, ResolvedType) InstantiateListToStoreElements( context.WriteComment("Add a Span local variable and initialize it with `CollectionsMarshal.AsSpan(list)`"); var spanToList = context.AddLocalVariableToCurrentMethod( "listSpan", - context.TypeResolver.ResolveAny(context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.LocalVariable).MakeGenericInstanceType(resolvedListTypeArgument)); + context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.LocalVariable).MakeGenericInstanceType(resolvedListTypeArgument)); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Call, - collectionMarshalTypeSymbol.GetMembers("AsSpan").OfType().Single().MethodResolverExpression(context).MakeGenericInstanceMethod(context, "AsSpan", [ resolvedListTypeArgument ])); + context.MemberResolver.MakeGeneticInstanceMethod(collectionMarshalTypeSymbol.GetMembers("AsSpan").OfType().Single().MethodResolverExpression(context), "AsSpan", [ resolvedListTypeArgument ])); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Stloc, new CilLocalVariableHandle(spanToList.VariableName)); return (spanToList, resolvedListTypeArgument); @@ -203,7 +162,7 @@ public static (DefinitionVariable, ResolvedType) InstantiateListToStoreElements( public static string GetSpanIndexerGetter(IVisitorContext context, ResolvedType typeArgument) { var methodVar = context.Naming.SyntheticVariable("getItem", ElementKind.Method); - var declaringType = context.TypeResolver.ResolveAny(context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.TypeReference).MakeGenericInstanceType(typeArgument); + var declaringType = context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemSpan, ResolveTargetKind.TypeReference).MakeGenericInstanceType(typeArgument); context.Generate($$"""var {{methodVar}} = new MethodReference("get_Item", {{context.TypeResolver.Bcl.System.Void}}, {{declaringType}}) { HasThis = true, ExplicitThis = false };"""); context.WriteNewLine(); context.Generate($"{methodVar}.Parameters.Add(new ParameterDefinition({context.TypeResolver.Bcl.System.Int32}));"); diff --git a/Cecilifier.Core/Misc/CecilifierContextBase.cs b/Cecilifier.Core/Misc/CecilifierContextBase.cs index 94037db7..ce0523bb 100644 --- a/Cecilifier.Core/Misc/CecilifierContextBase.cs +++ b/Cecilifier.Core/Misc/CecilifierContextBase.cs @@ -152,7 +152,7 @@ public void WriteNewLine() CecilifiedLineNumber++; } - public void WriteCilInstructionAfter(string ilVar, OpCode opCode, LinkedListNode after) + public void WriteCilInstructionAfter(IlContext ilVar, OpCode opCode, LinkedListNode after) { var toBeWritten = ApiDriver.EmitCilInstruction(this, ilVar, opCode, null); output.AddAfter(after, $"{indentation}{toBeWritten}{Environment.NewLine}"); diff --git a/Cecilifier.Core/Misc/CodeGenerationHelpers.cs b/Cecilifier.Core/Misc/CodeGenerationHelpers.cs index 80c45a5f..357ab86c 100644 --- a/Cecilifier.Core/Misc/CodeGenerationHelpers.cs +++ b/Cecilifier.Core/Misc/CodeGenerationHelpers.cs @@ -1,5 +1,5 @@ +using System.Linq; using System.Reflection.Emit; -using Cecilifier.Core.ApiDriver; using Cecilifier.Core.ApiDriver.Handles; using Microsoft.CodeAnalysis; using Cecilifier.Core.AST; @@ -11,12 +11,57 @@ namespace Cecilifier.Core.Misc; public struct CodeGenerationHelpers { - internal static DefinitionVariable StoreTopOfStackInLocalVariable(IVisitorContext context, string ilVar, string variableName, ITypeSymbol type) + internal static DefinitionVariable StoreTopOfStackInLocalVariable(IVisitorContext context, IlContext ilVar, string variableName, ITypeSymbol type) { var methodVar = context.DefinitionVariables.GetLastOf(VariableMemberKind.Method); - var resolvedVarType = context.TypeResolver.ResolveAny(type, ResolveTargetKind.LocalVariable); + var resolvedVarType = context.TypeResolver.Resolve(type, ResolveTargetKind.LocalVariable); var tempLocalDefinitionVariable = context.ApiDefinitionsFactory.LocalVariable(context, variableName, methodVar.VariableName, resolvedVarType); context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Stloc, new CilLocalVariableHandle(tempLocalDefinitionVariable.VariableName)); return tempLocalDefinitionVariable; } + + internal static void InstantiateDelegate(IVisitorContext context, IlContext ilVar, ITypeSymbol delegateType, string targetMethodExp, StaticDelegateCacheContext staticDelegateCacheContext) + { + // To match Roslyn implementation we need to cache static method do delegate conversions. + if (staticDelegateCacheContext.IsStaticDelegate) + { + staticDelegateCacheContext.EnsureCacheBackingFieldIsEmitted(context.TypeResolver.Resolve(delegateType, ResolveTargetKind.Field)); + LogWarningIfStaticMethodIsDeclaredInOtherType(context, staticDelegateCacheContext); + + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldsfld, staticDelegateCacheContext.CacheBackingField.AsToken()); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Dup); + + var cacheAlreadyInitializedTargetVarName = context.Naming.Label("cacheHit"); + context.ApiDriver.DefineLabel(context, ilVar, cacheAlreadyInitializedTargetVarName); + + context.WriteNewLine(); + context.ApiDriver.WriteCilBranch(context, ilVar, OpCodes.Brtrue, cacheAlreadyInitializedTargetVarName); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Pop); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldnull); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldftn, targetMethodExp.AsToken()); + var delegateCtor = delegateType.GetMembers().OfType().FirstOrDefault(m => m.Name == ".ctor"); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Newobj, delegateCtor.MethodResolverExpression(context).AsToken()); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Dup); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Stsfld, staticDelegateCacheContext.CacheBackingField.AsToken()); + + context.ApiDriver.MarkLabel(context, ilVar, cacheAlreadyInitializedTargetVarName); + context.WriteNewLine(); + } + else + { + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Ldftn, targetMethodExp.AsToken()); + var delegateCtor = delegateType.GetMembers().OfType().FirstOrDefault(m => m.Name == ".ctor"); + context.ApiDriver.WriteCilInstruction(context, ilVar, OpCodes.Newobj, delegateCtor.MethodResolverExpression(context).AsToken()); + } + } + + private static void LogWarningIfStaticMethodIsDeclaredInOtherType(IVisitorContext context, StaticDelegateCacheContext staticDelegateCacheContext) + { + var currentType = context.DefinitionVariables.GetLastOf(VariableMemberKind.Type); + if (currentType.IsValid && currentType.MemberName != staticDelegateCacheContext.Method.ContainingType.Name) + { + context.EmitWarning( + $"Converting static method ({staticDelegateCacheContext.Method.FullyQualifiedName()}) to delegate in a type other than the one defining it may generate incorrect code. Access type: {currentType.MemberName}, Method type: {staticDelegateCacheContext.Method.ContainingType.Name}"); + } + } } diff --git a/Cecilifier.Core/Misc/DefinitionsFactoryBase.cs b/Cecilifier.Core/Misc/DefinitionsFactoryBase.cs index cc8e1d7e..0f640aa5 100644 --- a/Cecilifier.Core/Misc/DefinitionsFactoryBase.cs +++ b/Cecilifier.Core/Misc/DefinitionsFactoryBase.cs @@ -24,15 +24,15 @@ protected static string RoslynToApiDriverModifiers(INamedTypeSymbol typeSymbol, { if (modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword))) { - typeAttributes = typeAttributes.AppendModifier(Constants.Cecil.StaticTypeAttributes); + typeAttributes = typeAttributes.AppendEnumFlag(Constants.Cecil.StaticTypeAttributes); modifiers = modifiers.Remove(SyntaxFactory.Token(SyntaxKind.StaticKeyword)); } - return typeAttributes.AppendModifier(RoslynToApiDriverModifier(modifiers, m => "TypeAttributes.Nested" + m.ValueText.PascalCase())).ToString(); + return typeAttributes.AppendEnumFlag(RoslynToApiDriverModifier(modifiers, m => "TypeAttributes.Nested" + m.ValueText.PascalCase())).ToString(); } var convertedModifiers = RoslynToApiDriverModifier(modifiers, "NotPublic", MapAttribute); - return typeAttributes.AppendModifier(convertedModifiers).ToString(); + return typeAttributes.AppendEnumFlag(convertedModifiers).ToString(); IEnumerable MapAttribute(SyntaxToken token) { @@ -72,7 +72,7 @@ private static void AppendStructLayoutTo(ITypeSymbol typeSymbol, StringBuilder t if (!typeSymbol.TryGetAttribute(out var structLayoutAttribute)) { - typeAttributes.AppendModifier("TypeAttributes.SequentialLayout"); + typeAttributes.AppendEnumFlag("TypeAttributes.SequentialLayout"); } else { @@ -84,7 +84,7 @@ private static void AppendStructLayoutTo(ITypeSymbol typeSymbol, StringBuilder t _ => throw new ArgumentException($"Invalid StructLayout value for {typeSymbol.Name}") }; - typeAttributes.AppendModifier(specifiedLayout); + typeAttributes.AppendEnumFlag(specifiedLayout); } } @@ -105,13 +105,13 @@ private static string RoslynToApiDriverModifier( var modifierStr = finalModifierList .SelectMany(mapAttribute) .Where(attr => !string.IsNullOrEmpty(attr)) - .Aggregate(new StringBuilder(), (acc, curr) => acc.AppendModifier($"{targetEnum}.{curr}")); + .Aggregate(new StringBuilder(), (acc, curr) => acc.AppendEnumFlag($"{targetEnum}.{curr}")); modifierStr.Append(accessibilityModifiers); if (!syntaxTokens.Any(m => m.IsKind(SyntaxKind.PrivateKeyword) || m.IsKind(SyntaxKind.InternalKeyword) || m.IsKind(SyntaxKind.PrivateKeyword) || m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.ProtectedKeyword))) - modifierStr.AppendModifier($"{targetEnum}.{defaultAccessibility}"); + modifierStr.AppendEnumFlag($"{targetEnum}.{defaultAccessibility}"); return modifierStr.ToString(); @@ -143,7 +143,7 @@ private static string RoslynToApiDriverModifier(IEnumerable modifie { var cecilModifierStr = modifiers.Aggregate(new StringBuilder(), (acc, token) => { - acc.AppendModifier(map(token)); + acc.AppendEnumFlag(map(token)); return acc; }); diff --git a/Cecilifier.Core/Misc/StaticDelegateCacheContext.cs b/Cecilifier.Core/Misc/StaticDelegateCacheContext.cs index 1a75dc86..f4b1264c 100644 --- a/Cecilifier.Core/Misc/StaticDelegateCacheContext.cs +++ b/Cecilifier.Core/Misc/StaticDelegateCacheContext.cs @@ -69,7 +69,7 @@ public string EnsureCacheBackingFieldIsEmitted(ResolvedType delegateType) cacheTypeVar.Properties[counterName] = ++staticMethodToDelegateConversionCount; CacheBackingField = context.Naming.SyntheticVariable("cachedDelegate", ElementKind.Field); - var fieldExps = context.ApiDefinitionsFactory.Field(context, new MemberDefinitionContext(backingFieldName, CacheBackingField, (string)cacheTypeVar), cacheInnerTypeName, delegateType, Constants.Cecil.StaticFieldAttributes, false, false, null); + var fieldExps = context.ApiDefinitionsFactory.Field(context, new MemberDefinitionContext(backingFieldName, $"{Method.Name}_cachingField",CacheBackingField, (string)cacheTypeVar), cacheInnerTypeName, delegateType, Constants.Cecil.StaticFieldAttributes, false, false, null); context.Generate(fieldExps); return CacheBackingField; @@ -80,10 +80,10 @@ private DefinitionVariable EmitCacheType(string cacheTypeName) var cachedTypeVar = context.Naming.Type("", ElementKind.Class); var outerTypeVariable = context.DefinitionVariables.GetVariable(Method.ContainingType.ToDisplayString(), VariableMemberKind.Type, Method.ContainingType.ContainingSymbol.ToDisplayString()); - string attrs = Constants.Cecil.StaticClassAttributes.AppendModifier("TypeAttributes.NestedPrivate"); + var attrs = Constants.Cecil.StaticClassAttributes.AppendEnumFlag("TypeAttributes.NestedPrivate"); var cacheTypeExps = context.ApiDefinitionsFactory.Type( context, - new MemberDefinitionContext(cacheTypeName, cachedTypeVar, outerTypeVariable.IsValid ? outerTypeVariable.VariableName : null), + new MemberDefinitionContext(cacheTypeName, "cache", cachedTypeVar, outerTypeVariable.IsValid ? outerTypeVariable.VariableName : null), DeclaringTypeNamespace, attrs, context.RoslynTypeSystem.SystemObject, diff --git a/Cecilifier.Core/Misc/Utils.cs b/Cecilifier.Core/Misc/Utils.cs index 0dfe0a0f..3e7462ea 100644 --- a/Cecilifier.Core/Misc/Utils.cs +++ b/Cecilifier.Core/Misc/Utils.cs @@ -1,7 +1,6 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using Cecilifier.Core.AST; using Cecilifier.Core.Extensions; +using Cecilifier.Core.TypeSystem; using Microsoft.CodeAnalysis; #nullable enable @@ -11,29 +10,22 @@ public struct Utils { public static string ConstructorMethodName(bool isStatic) => $".{(isStatic ? Constants.Cecil.StaticConstructorName : Constants.Cecil.InstanceConstructorName)}"; - //TODO: Move to Cecil related code (Cecilifier.ApiDriver.MonoCecil project) - public static string ImportFromMainModule(string expression) => $"assembly.MainModule.ImportReference({expression})"; - - public static string MakeGenericTypeIfAppropriate(IVisitorContext context, ISymbol memberSymbol, string backingFieldVar, string memberDeclaringTypeVar) + public static string? MakeGenericTypeIfAppropriate(IVisitorContext context, IEventSymbol memberSymbol, string fieldName) { - if (!(memberSymbol.ContainingSymbol is INamedTypeSymbol ts) || !ts.IsGenericType || !memberSymbol.IsDefinedInCurrentAssembly(context)) - return backingFieldVar; - - var genTypeVar = context.Naming.GenericInstance(memberSymbol); - context.Generate($"var {genTypeVar} = {memberDeclaringTypeVar}.MakeGenericInstanceType({memberDeclaringTypeVar}.GenericParameters.ToArray());"); - context.WriteNewLine(); + if (!(memberSymbol.Type is INamedTypeSymbol ts) || !ts.IsGenericType || !memberSymbol.IsDefinedInCurrentAssembly(context)) + return null; + var openTypeRef = context.TypeResolver.Resolve(memberSymbol.Type.OriginalDefinition, ResolveTargetKind.LocalVariable); + var declaringType = context.TypeResolver.Resolve(memberSymbol.ContainingType, ResolveTargetKind.LocalVariable); + var instantiatedGenericType = context.TypeResolver.MakeGenericInstanceType(memberSymbol.Type.OriginalDefinition.NameIncludingTypeParametersAndArguments(), openTypeRef, ts, ResolveTargetKind.LocalVariable); + var fieldRefVar = context.Naming.MemberReference("fld_"); - context.Generate($"var {fieldRefVar} = new FieldReference({backingFieldVar}.Name, {backingFieldVar}.FieldType, {genTypeVar});"); - context.WriteNewLine(); + var fieldRefStatements = context.ApiDefinitionsFactory.FieldReference(context, fieldRefVar, fieldName, instantiatedGenericType, declaringType); + context.Generate(fieldRefStatements); return fieldRefVar; } - public static T EnsureNotNull([NotNullIfNotNull(nameof(node))] T? node, [CallerArgumentExpression("node")] string? msg = null) where T : SyntaxNode - { - return node.EnsureNotNull(); - } public static string BackingFieldNameForAutoProperty(string propertyName) => $"<{propertyName}>k__BackingField"; } } diff --git a/Cecilifier.Core/TypeSystem/IMemberResolver.cs b/Cecilifier.Core/TypeSystem/IMemberResolver.cs index 918cd643..f9960e3b 100644 --- a/Cecilifier.Core/TypeSystem/IMemberResolver.cs +++ b/Cecilifier.Core/TypeSystem/IMemberResolver.cs @@ -6,11 +6,26 @@ namespace Cecilifier.Core.TypeSystem; public interface IMemberResolver { - // Returns an expression? that represents the resolved method + /// Returns an expression representing the resolve method. + /// + /// The returned expression may be the name of a local variable that has been previously + /// initialized with the correct expression to be used. + /// Some Api Drivers (for example, Mono.Cecil) will generally return a 'real' expression, + /// while others (like System.Reflection.Metadata) will store the final expression in a + /// local variable (either to avoid noise/code duplication or due to the way it needs + /// to emit the code) and return the name of the local variable. + /// + /// + /// Returns an expression that represents the resolved method. string ResolveMethod(IMethodSymbol method); - string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodNameForVariableRegistration, ResolvedType returnType, IReadOnlyList parameters, int typeParameterCountCount, MemberOptions options); + string ResolveMethod(string declaringTypeName, string declaringTypeVariable, string methodName, ResolvedType returnType, IReadOnlyList parameters, IReadOnlyList typeParameters, MemberOptions options); string ResolveDefaultConstructor(ITypeSymbol baseType, string derivedTypeVar); string ResolveField(IFieldSymbol field); string ResolveEventField(IEventSymbol aEvent); + string ImportReference(string expression); + + string MakeGeneticInstanceMethod(string methodReferenceVariable, string methodName, IReadOnlyList resolvedTypeArguments); + + string MapSpecificAttributes(IMethodSymbol method); } diff --git a/Cecilifier.Core/TypeSystem/ITypeResolver.cs b/Cecilifier.Core/TypeSystem/ITypeResolver.cs index a6bb09e8..8a5c4f1b 100644 --- a/Cecilifier.Core/TypeSystem/ITypeResolver.cs +++ b/Cecilifier.Core/TypeSystem/ITypeResolver.cs @@ -1,30 +1,41 @@ #nullable enable +using System; using Microsoft.CodeAnalysis; namespace Cecilifier.Core.TypeSystem; public interface ITypeResolver { - ResolvedType ResolveAny(ITypeSymbol type, in TypeResolutionContext resolutionContext); + ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext); ResolvedType ResolvePredefinedType(ITypeSymbol type, in TypeResolutionContext resolutionContext); ResolvedType ResolveLocalVariableType(ITypeSymbol type, in TypeResolutionContext context); - ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext); + + /// + /// Resolves a generic type parameter from a + /// + /// + /// + /// + /// + /// Api Drivers which represents 'Generic Type Parameters' as instances of types this method must simply return + /// Others must return a valid expression (variable name, method call, etc) representing the generic type parameter. + /// + ResolvedType ResolveTypeParameter(ResolvedType genericTypeParameter, TypeParameterKind typeParameterKind, in TypeResolutionContext resolutionContext); /// /// Some Api drivers may use different syntaxes depending on the usage (i.e. when being used to declare - /// a local variable, when being used as the base type of a class, when being used as a type parameter, etc). - /// - /// Some Api drivers will simply return the as is."/> + /// a local variable, as the base type of classes, as a type parameter, etc). /// /// the name of the variable representing a type reference. /// context to be considered when applying syntax + /// Some Api drivers will simply return the as is."/> /// an expression valid to be used in the specified ResolvedType ApplySpecificSyntax(string variableName, in TypeResolutionContext resolutionContext); - ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResolutionContext resolutionContext); - - ResolvedType MakeGenericInstanceType(ResolvedType typeReference, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext); + ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext); + ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, Span typeArguments, in TypeResolutionContext resolutionContext); + ResolvedType MakeByRefType(in ResolvedType resolvedType); Bcl Bcl { get; } } diff --git a/Cecilifier.Core/TypeSystem/ParameterSpec.cs b/Cecilifier.Core/TypeSystem/ParameterSpec.cs index 6d77c850..53ab254c 100644 --- a/Cecilifier.Core/TypeSystem/ParameterSpec.cs +++ b/Cecilifier.Core/TypeSystem/ParameterSpec.cs @@ -6,7 +6,7 @@ namespace Cecilifier.Core.TypeSystem; -public record ParameterSpec(string Name, ResolvedType ElementType, RefKind RefKind, string Attributes, string? DefaultValue = null, Func? ElementTypeResolver = null) +public record ParameterSpec(string Name, ResolvedType ElementType, RefKind RefKind, string Attributes, string? DefaultValue = null, Func? ElementTypeResolver = null) { public virtual ResolvedType ElementType { get; } = ElementType; public string? RegistrationTypeName { get; init; } @@ -15,7 +15,7 @@ public record ParameterSpec(string Name, ResolvedType ElementType, RefKind RefKi public record ParameterSymbolParameterSpec(IParameterSymbol Parameter, IVisitorContext Context) : ParameterSpec(Parameter.Name, string.Empty, Parameter.RefKind, Constants.ParameterAttributes.None) { - public override ResolvedType ElementType => Context.TypeResolver.ResolveAny(Parameter.Type, ResolveTargetKind.Parameter); + public override ResolvedType ElementType => Context.TypeResolver.Resolve(Parameter.Type, ResolveTargetKind.Parameter); public override string? ParamsAttributeName => Parameter.ParamsAttributeMatchingType(); } diff --git a/Cecilifier.Core/TypeSystem/ResolveTargetKind.cs b/Cecilifier.Core/TypeSystem/ResolveTargetKind.cs index 81d1d921..14782230 100644 --- a/Cecilifier.Core/TypeSystem/ResolveTargetKind.cs +++ b/Cecilifier.Core/TypeSystem/ResolveTargetKind.cs @@ -3,7 +3,9 @@ namespace Cecilifier.Core.TypeSystem; public enum ResolveTargetKind { None, - ArrayElementType, // Any enum values equals to or smaller than `ArrayElementType` have special handling when resolving types. + ComposedElementType, // `Composed Types` are types like arrays, pointer, references, etc. The element type of a composed type is the type being decorated. For instance, + // for an `string[]` (an array of strings), the element type is `string` + // Any enum values equals to or smaller than `ComposeElementType` have special handling when resolving types. Field, LocalVariable, @@ -13,5 +15,6 @@ public enum ResolveTargetKind TypeReference, AttributeNamedArgument, AttributeArgument, - GenericTypeArgument + GenericTypeArgument, + GenericTypeParameterConstraint } diff --git a/Cecilifier.Core/TypeSystem/ResolvedType.cs b/Cecilifier.Core/TypeSystem/ResolvedType.cs index d53418d0..a6f5ef2b 100644 --- a/Cecilifier.Core/TypeSystem/ResolvedType.cs +++ b/Cecilifier.Core/TypeSystem/ResolvedType.cs @@ -8,7 +8,7 @@ namespace Cecilifier.Core.TypeSystem; /// /// /// A resolved type can be either the name of a variable in the generated code that stores an expression returned by one of the methods of -/// or the expression itself. +/// or an expression itself. /// [DebuggerDisplay( "Valid: {(bool) this}, Expression: {Expression}")] public readonly record struct ResolvedType diff --git a/Cecilifier.Core/TypeSystem/RoslynTypeSystem.cs b/Cecilifier.Core/TypeSystem/RoslynTypeSystem.cs index 9c5a5700..6db69f4b 100644 --- a/Cecilifier.Core/TypeSystem/RoslynTypeSystem.cs +++ b/Cecilifier.Core/TypeSystem/RoslynTypeSystem.cs @@ -85,7 +85,7 @@ public RoslynTypeSystem(IVisitorContext ctx) public ITypeSymbol SystemValueType { get; } public ITypeSymbol SystemRuntimeCompilerServicesRuntimeHelpers { get; } public ITypeSymbol SystemRuntimeCompilerServicesCompilerGeneratedAttribute { get; } - public ITypeSymbol SystemNullableOfT { get; } + public INamedTypeSymbol SystemNullableOfT { get; } public ITypeSymbol SystemRuntimeCompilerServicesUnsafe { get; } public ITypeSymbol SystemRuntimeInteropServicesMemoryMarshal { get; } public ITypeSymbol SystemCollectionsGenericICollectionOfT { get; } diff --git a/Cecilifier.Core/TypeSystem/SystemTypeSystem.cs b/Cecilifier.Core/TypeSystem/SystemTypeSystem.cs index 601d03f3..2ec70bfd 100644 --- a/Cecilifier.Core/TypeSystem/SystemTypeSystem.cs +++ b/Cecilifier.Core/TypeSystem/SystemTypeSystem.cs @@ -17,12 +17,12 @@ public SystemTypeSystem(ITypeResolver typeResolver, IVisitorContext context) [SpecialType.System_Void] = typeResolver.ResolvePredefinedType(context.RoslynTypeSystem.SystemVoid, ResolveTargetKind.TypeReference), [SpecialType.System_Object] = typeResolver.ResolvePredefinedType(context.RoslynTypeSystem.SystemObject, ResolveTargetKind.TypeReference), [SpecialType.System_Boolean] = typeResolver.ResolvePredefinedType(context.RoslynTypeSystem.SystemBoolean, ResolveTargetKind.TypeReference), - [SpecialType.System_Enum] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), - [SpecialType.System_ValueType] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), - [SpecialType.System_MulticastDelegate] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), - [SpecialType.System_AsyncCallback] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), - [SpecialType.System_IAsyncResult] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), - [SpecialType.System_Nullable_T] = typeResolver.ResolveAny(context.RoslynTypeSystem.ForType>().OriginalDefinition, ResolveTargetKind.TypeReference), + [SpecialType.System_Enum] = typeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), + [SpecialType.System_ValueType] = typeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), + [SpecialType.System_MulticastDelegate] = typeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), + [SpecialType.System_AsyncCallback] = typeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), + [SpecialType.System_IAsyncResult] = typeResolver.Resolve(context.RoslynTypeSystem.ForType(), ResolveTargetKind.TypeReference), + [SpecialType.System_Nullable_T] = typeResolver.Resolve(context.RoslynTypeSystem.ForType>().OriginalDefinition, ResolveTargetKind.TypeReference), }; } diff --git a/Cecilifier.Core/TypeSystem/TypeResolutionOptions.cs b/Cecilifier.Core/TypeSystem/TypeResolutionOptions.cs index 1ba88b80..f00ec512 100644 --- a/Cecilifier.Core/TypeSystem/TypeResolutionOptions.cs +++ b/Cecilifier.Core/TypeSystem/TypeResolutionOptions.cs @@ -9,4 +9,9 @@ public enum TypeResolutionOptions None = 0x0, IsByRef = 0x1 << 0, IsValueType = 0x1 << 1, + + /// Register the final local variable emitted to store the newly resolved type. This avoids code bloating. + RegisterVariables = 0x1 << 2, + + OpenGenericType = 0x1 << 3, // when resolving types, resolve the open generic type as opposed to the closed one. } diff --git a/Cecilifier.Core/TypeSystem/TypeResolverBase.cs b/Cecilifier.Core/TypeSystem/TypeResolverBase.cs index 044de74d..c411188a 100644 --- a/Cecilifier.Core/TypeSystem/TypeResolverBase.cs +++ b/Cecilifier.Core/TypeSystem/TypeResolverBase.cs @@ -20,7 +20,7 @@ protected TypeResolverBase(TContext context) public Bcl Bcl { get; } // Resolvers for api drivers that need to generate different code based on the target can override this method. - public virtual ResolvedType ResolveAny(ITypeSymbol type, in TypeResolutionContext resolutionContext) + public virtual ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext) { var resolvedType = ResolveLocalVariableType(type, in resolutionContext); if (resolvedType) @@ -42,15 +42,16 @@ public virtual ResolvedType ResolveAny(ITypeSymbol type, in TypeResolutionContex if (resolvedType) return resolvedType; - return Resolve(type, in resolutionContext); + return ResolveFromAssembly(type, in resolutionContext); } public virtual ResolvedType ApplySpecificSyntax(string variableName, in TypeResolutionContext resolutionContext) => variableName; - public abstract ResolvedType Resolve(ITypeSymbol type, in TypeResolutionContext resolutionContext); public abstract ResolvedType ResolvePredefinedType(ITypeSymbol type, in TypeResolutionContext resolutionContext); public abstract ResolvedType MakeArrayType(ITypeSymbol elementType, in TypeResolutionContext resolutionContext); protected abstract ResolvedType MakePointerType(ITypeSymbol pointerType, in TypeResolutionContext resolutionContext); protected abstract ResolvedType MakeFunctionPointerType(IFunctionPointerTypeSymbol functionPointer, in TypeResolutionContext resolutionContext); + protected abstract ResolvedType ResolveTypeParameter(ITypeSymbol type, in TypeResolutionContext resolutionContext); + public abstract ResolvedType ResolveTypeParameter(ResolvedType genericTypeParameter, TypeParameterKind typeParameterKind, in TypeResolutionContext resolutionContext); public virtual ResolvedType ResolveLocalVariableType(ITypeSymbol type, in TypeResolutionContext context) { @@ -60,25 +61,23 @@ public virtual ResolvedType ResolveLocalVariableType(ITypeSymbol type, in TypeRe if (found != null && type is INamedTypeSymbol { IsGenericType: true } genericTypeSymbol) { - return MakeGenericInstanceType(found, genericTypeSymbol, new TypeResolutionContext(context.TargetKind, context.Options, found)); + return MakeGenericInstanceType(genericTypeSymbol.NameIncludingTypeParametersAndArguments(),found, genericTypeSymbol, new TypeResolutionContext(context.TargetKind, context.Options, found)); } return new ResolvedType(found); } + + protected abstract ResolvedType ResolveNestedTypeCore(INamedTypeSymbol type, in TypeResolutionContext resolutionContext); private ResolvedType ResolveNestedType(ITypeSymbol type, in TypeResolutionContext resolutionContext) { if (type.ContainingType == null || type.Kind == SymbolKind.TypeParameter) return null; - if (type is INamedTypeSymbol { IsGenericType: true } nestedType - && (nestedType.HasTypeArgumentOfTypeFromCecilifiedCodeTransitive(_context) || nestedType.ContainingType.IsTypeParameterOrIsGenericTypeReferencingTypeParameter())) + if (type is INamedTypeSymbol { IsGenericType: true } genericNestedType) { - // collects the type arguments for all types in the parent chain. - var typeArguments = nestedType.GetAllTypeArguments().ToArray(); - var resolveNestedType = new ResolvedType($"""TypeHelpers.NewRawNestedTypeReference("{type.Name}", module: assembly.MainModule, {ResolveAny(type.ContainingType.OriginalDefinition, in resolutionContext)}, isValueType: {type.IsValueType.ToKeyword()}, {typeArguments.Length})"""); - - // if type is a generic type definition we return the open, resolved type + var resolveNestedType = ResolveNestedTypeCore(genericNestedType, in resolutionContext); + // if type is a generic type definition, we return the open, resolved type // otherwise this method is expected to return a 'GenericInstanceType'. // Note that in this case even if the parent type is the generic one, // in IL, we need to create a 'GenericInstanceType' of the nested @@ -89,7 +88,7 @@ private ResolvedType ResolveNestedType(ITypeSymbol type, in TypeResolutionContex // and the parent's type generic parameters are added to nested types) return type.IsDefinition ? resolveNestedType - : resolveNestedType.MakeGenericInstanceType(typeArguments.Select(t => _context.TypeResolver.ResolveAny(t, new TypeResolutionContext(ResolveTargetKind.None, TypeResolutionOptions.None))).ToArray()); + : MakeGenericInstanceType(genericNestedType.NameIncludingTypeParametersAndArguments(), resolveNestedType, genericNestedType, in resolutionContext); } return null; @@ -111,41 +110,10 @@ private ResolvedType ResolvePredefinedAndComposedTypes(ITypeSymbol type, in Type { return MakeFunctionPointerType(functionPointer, in resolutionContext); } - - if (type.SpecialType == SpecialType.None - || type.SpecialType == SpecialType.System_Array - || type.SpecialType == SpecialType.System_Enum - || type.SpecialType == SpecialType.System_ValueType - || type.SpecialType == SpecialType.System_Decimal - || type.SpecialType == SpecialType.System_DateTime - || type.SpecialType == SpecialType.System_Delegate - || type.SpecialType == SpecialType.System_MulticastDelegate - || type.SpecialType == SpecialType.System_AsyncCallback - || type.SpecialType == SpecialType.System_RuntimeTypeHandle - || type.TypeKind == TypeKind.Interface) - { - return null; - } - return ResolvePredefinedType(type, in resolutionContext); - } - - private ResolvedType ResolveTypeParameter(ITypeSymbol type, in TypeResolutionContext resolutionContext) - { - if (type is not ITypeParameterSymbol typeParameterSymbol) - return null; - - if (resolutionContext.TypeParameterProviderVar == null) - return null; - - var resolvedType = typeParameterSymbol.ContainingSymbol.Kind switch - { - SymbolKind.NamedType => $"(({resolutionContext.TypeParameterProviderVar} is MethodReference methodReference) ? ((GenericInstanceType) methodReference.DeclaringType).ElementType : (IGenericParameterProvider) {resolutionContext.TypeParameterProviderVar} ).GenericParameters[{typeParameterSymbol.Ordinal}]", - SymbolKind.Method => $"{resolutionContext.TypeParameterProviderVar}.GenericParameters[{typeParameterSymbol.Ordinal}]", - _ => null - }; - - return new ResolvedType(resolvedType); + return type.IsConsideredPreDefinedType() + ? ResolvePredefinedType(type, in resolutionContext) + : null; } private ResolvedType ResolveGenericType(ITypeSymbol type, in TypeResolutionContext resolutionContext) @@ -160,10 +128,10 @@ private ResolvedType ResolveGenericType(ITypeSymbol type, in TypeResolutionConte return null; } - var genericType = Resolve(genericTypeSymbol.ConstructedFrom, in resolutionContext); - return genericTypeSymbol.IsDefinition + var genericType = ResolveFromAssembly(genericTypeSymbol.ConstructedFrom, in resolutionContext); + return genericTypeSymbol.IsDefinition || resolutionContext.Options.HasFlag(TypeResolutionOptions.OpenGenericType) ? genericType - : MakeGenericInstanceType(genericType, genericTypeSymbol, in resolutionContext); + : MakeGenericInstanceType(genericTypeSymbol.NameIncludingTypeParametersAndArguments(), genericType, genericTypeSymbol, in resolutionContext); } protected ReadOnlySpan CollectTypeArguments(INamedTypeSymbol typeArgumentProvider, ref Buffer256 collectTo) @@ -183,6 +151,11 @@ protected ReadOnlySpan CollectTypeArguments(INamedTypeSymbol typeAr return typeArguments.Slice(0, count); } - public abstract ResolvedType MakeGenericInstanceType(ResolvedType typeReference, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext); + public abstract ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, INamedTypeSymbol genericTypeSymbol, in TypeResolutionContext resolutionContext); + public abstract ResolvedType MakeGenericInstanceType(string typeName, ResolvedType openGenericType, Span typeArguments, in TypeResolutionContext resolutionContext); + + public abstract ResolvedType MakeByRefType(in ResolvedType resolvedType); + + protected abstract ResolvedType ResolveFromAssembly(ITypeSymbol type, in TypeResolutionContext resolutionContext); } } diff --git a/Cecilifier.Core/Variables/DefinitionVariable.cs b/Cecilifier.Core/Variables/DefinitionVariable.cs index 18654444..ed1da32c 100644 --- a/Cecilifier.Core/Variables/DefinitionVariable.cs +++ b/Cecilifier.Core/Variables/DefinitionVariable.cs @@ -19,7 +19,7 @@ public DefinitionVariable(string parentTypeName, string memberName, VariableMemb public string MemberName { get; } public VariableMemberKind Kind { get; } public string VariableName { get; } - public bool IsValid { get; private set; } + public bool IsValid { get; protected set; } public bool IsForwarded { get; internal set; } public IDictionary Properties { get; } = new Dictionary(); diff --git a/Cecilifier.Core/Variables/DefinitionVariableManager.cs b/Cecilifier.Core/Variables/DefinitionVariableManager.cs index 379c5e20..da603b1a 100644 --- a/Cecilifier.Core/Variables/DefinitionVariableManager.cs +++ b/Cecilifier.Core/Variables/DefinitionVariableManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Cecilifier.Core.AST; +using Cecilifier.Core.TypeSystem; namespace Cecilifier.Core.Variables; @@ -19,9 +20,9 @@ public MethodDefinitionVariable RegisterMethod(MethodDefinitionVariable variable return variable; } - public MethodDefinitionVariable RegisterMethod(string parentName, string methodName, string[] parameterTypes, int typeParameterCount, string definitionVariableName) + public MethodDefinitionVariable RegisterMethod(string parentName, string methodName, string[] parameterTypes, string[] typeParameters, string definitionVariableName) { - var definitionVariable = new MethodDefinitionVariable(parentName, methodName, parameterTypes, typeParameterCount, definitionVariableName); + var definitionVariable = new MethodDefinitionVariable(parentName, methodName, parameterTypes, typeParameters, definitionVariableName); RegisterVariable(definitionVariable); return definitionVariable; } @@ -67,6 +68,18 @@ public DefinitionVariable GetVariable(string memberName, VariableMemberKind vari return DefinitionVariable.NotFound; } + public DefinitionVariable GetOrRegisterNonMethodVariable(string memberName, string parentName, VariableMemberKind variableMemberKind, in TypeResolutionContext resolutionContext, TState state, Func registerFunction) where TState : allows ref struct + { + var found = (resolutionContext.Options & TypeResolutionOptions.RegisterVariables) == TypeResolutionOptions.RegisterVariables + ? GetVariable(memberName, variableMemberKind, parentName) + : new DefinitionVariable(parentName, memberName, variableMemberKind, registerFunction(state)); + + if (found.IsValid) + return found; + + return RegisterNonMethod(parentName, memberName, variableMemberKind, registerFunction(state)); + } + public DefinitionVariable GetLastOf(VariableMemberKind kind) { var index = _definitionStack.FindLastIndex(c => c.Kind == kind); @@ -78,9 +91,9 @@ public DefinitionVariable GetLastOf(VariableMemberKind kind) } public IEnumerable GetVariablesOf(VariableMemberKind kind) => _definitionVariables.Where(candidate => candidate.Kind == kind); - public ScopedDefinitionVariable WithCurrentMethod(string parentName, string memberName, string[] paramTypes, int typeParameterCount, string definitionVariableName) + public ScopedDefinitionVariable WithCurrentMethod(string parentName, string memberName, string[] paramTypes, string[] typeParameters, string definitionVariableName) { - var registered = RegisterMethod(parentName, memberName, paramTypes, typeParameterCount, definitionVariableName); + var registered = RegisterMethod(parentName, memberName, paramTypes, typeParameters, definitionVariableName); return WithVariable(registered); } @@ -143,7 +156,7 @@ public ScopedDefinitionVariable EnterLocalScope() return new ScopedDefinitionVariable(_definitionVariables, _definitionVariables.Count, true); } - public void RegisterDependentOnRegistration(string targetVariable, IVisitorContext context, Action toExecute, object state) + public void RegisterDependentOnRegistration(string targetVariable, IVisitorContext context, Action toExecute, object? state) { if (!_executeUponRegistration.TryGetValue(targetVariable, out var toExecuteList)) { @@ -167,5 +180,6 @@ private void RegisterVariable(DefinitionVariable definitionVariable) _definitionVariables.Add(definitionVariable); } - private record struct ExecuteUponRegistrationState(IVisitorContext Context, Action Function, object State); + private record struct ExecuteUponRegistrationState(IVisitorContext Context, Action Function, object? State); + } diff --git a/Cecilifier.Core/Variables/MethodDefinitionVariable.cs b/Cecilifier.Core/Variables/MethodDefinitionVariable.cs index 34f9ac16..5077507f 100644 --- a/Cecilifier.Core/Variables/MethodDefinitionVariable.cs +++ b/Cecilifier.Core/Variables/MethodDefinitionVariable.cs @@ -1,30 +1,31 @@ using System; using System.Diagnostics.CodeAnalysis; +using Cecilifier.Core.Extensions; namespace Cecilifier.Core.Variables; [ExcludeFromCodeCoverage] public class MethodDefinitionVariable : DefinitionVariable, IEquatable { - public static readonly MethodDefinitionVariable MethodNotFound = new MethodDefinitionVariable(string.Empty, string.Empty, [], 0); + public static readonly MethodDefinitionVariable MethodNotFound = new(string.Empty, string.Empty, [], []) { IsValid = false }; - public MethodDefinitionVariable(string parentTypeName, string methodName, string[] parameterTypeNames, int typeParameterCountCount, string variableName = null) - : this(VariableMemberKind.Method, parentTypeName, methodName, parameterTypeNames, typeParameterCountCount, variableName) + public MethodDefinitionVariable(string parentTypeName, string methodName, string[] parameterTypeNames, string[] typeParameters, string variableName = null) + : this(VariableMemberKind.Method, parentTypeName, methodName, parameterTypeNames, typeParameters, variableName) { } - public MethodDefinitionVariable(VariableMemberKind methodKind, string parentTypeName, string methodName, string[] parameterTypeNames, int typeParameterCountCount, string variableName = null) + public MethodDefinitionVariable(VariableMemberKind methodKind, string parentTypeName, string methodName, string[] parameterTypeNames, string[] typeParameters, string variableName = null) : base(parentTypeName, methodName, methodKind, variableName) { Parameters = parameterTypeNames; - TypeParameterCount = typeParameterCountCount; + TypeParameters = typeParameters; } - public MethodDefinitionVariable WithVariableName(string variableName) => new(Kind, ParentName, MemberName, Parameters, TypeParameterCount, variableName); + public MethodDefinitionVariable WithVariableName(string variableName) => new(Kind, ParentName, MemberName, Parameters, TypeParameters, variableName); private string[] Parameters { get; } - private int TypeParameterCount { get; } + private string[] TypeParameters { get; } public bool Equals(MethodDefinitionVariable other) { @@ -56,7 +57,7 @@ public bool Equals(MethodDefinitionVariable other) } } - return TypeParameterCount == other.TypeParameterCount; + return TypeParameters.ComputeHashCode() == other.TypeParameters.ComputeHashCode(); } public static bool operator ==(MethodDefinitionVariable lhs, MethodDefinitionVariable rhs) @@ -97,8 +98,8 @@ public override int GetHashCode() unchecked { return (base.GetHashCode() * 397) - ^ (Parameters != null ? Parameters.GetHashCode() : 0) - ^ TypeParameterCount.GetHashCode(); + ^ Parameters.ComputeHashCode() + ^ TypeParameters.ComputeHashCode(); } } diff --git a/Cecilifier.Core/Variables/VariableMemberKind.cs b/Cecilifier.Core/Variables/VariableMemberKind.cs index 99033dd6..b9a131cc 100644 --- a/Cecilifier.Core/Variables/VariableMemberKind.cs +++ b/Cecilifier.Core/Variables/VariableMemberKind.cs @@ -15,4 +15,5 @@ public enum VariableMemberKind ModuleReference = 1 << 7, MethodSignature = 1 << 8, MethodReference = 1 << 9, + MethodInstantiation = 1 << 10, } diff --git a/Cecilifier.TypeMapGenerator/Cecilifier.TypeMapGenerator.csproj b/Cecilifier.TypeMapGenerator/Cecilifier.TypeMapGenerator.csproj index 4641cfcd..4f5d1e39 100644 --- a/Cecilifier.TypeMapGenerator/Cecilifier.TypeMapGenerator.csproj +++ b/Cecilifier.TypeMapGenerator/Cecilifier.TypeMapGenerator.csproj @@ -15,9 +15,9 @@ - - - + + + @@ -25,6 +25,6 @@ - + diff --git a/Cecilifier.Web/CecilifierModel.cs b/Cecilifier.Web/CecilifierModel.cs index cf590003..65e47e15 100644 --- a/Cecilifier.Web/CecilifierModel.cs +++ b/Cecilifier.Web/CecilifierModel.cs @@ -8,7 +8,7 @@ public class CecilifierModel Exe - net9.0 + net10.0 {0} """; diff --git a/Cecilifier.Web/CecilifierRequest.cs b/Cecilifier.Web/CecilifierRequest.cs index 26db9eb4..c6b49f0e 100644 --- a/Cecilifier.Web/CecilifierRequest.cs +++ b/Cecilifier.Web/CecilifierRequest.cs @@ -1,5 +1,4 @@ using System.Text.Json.Serialization; -using Newtonsoft.Json; namespace Cecilifier.Web { diff --git a/Cecilifier.Web/Pages/Index.cshtml b/Cecilifier.Web/Pages/Index.cshtml index c30c3bcb..eb17fea8 100644 --- a/Cecilifier.Web/Pages/Index.cshtml +++ b/Cecilifier.Web/Pages/Index.cshtml @@ -63,16 +63,16 @@ - - + +
- - + +
-
- + - + + + diff --git a/Cecilifier.Web/Pages/Shared/_Layout.cshtml b/Cecilifier.Web/Pages/Shared/_Layout.cshtml index be1deeed..d7dbc1bb 100644 --- a/Cecilifier.Web/Pages/Shared/_Layout.cshtml +++ b/Cecilifier.Web/Pages/Shared/_Layout.cshtml @@ -29,10 +29,15 @@ + + + + + - + Cecilifier - Learn Mono.Cecil by example @@ -126,7 +131,7 @@ - | © 2025 - Adriano Carlos Verona + | © 2026 - Adriano Carlos Verona
diff --git a/Cecilifier.Web/wwwroot/js/cecilifier.assemblyreferences.js b/Cecilifier.Web/wwwroot/js/cecilifier.assemblyreferences.js index dbd9c611..84066a2e 100644 --- a/Cecilifier.Web/wwwroot/js/cecilifier.assemblyreferences.js +++ b/Cecilifier.Web/wwwroot/js/cecilifier.assemblyreferences.js @@ -155,7 +155,7 @@ function computeSHA256(base64Contents) { function StoreReferenceAssembliesLocallyAndClose() { storeReferenceAssembliesLocally(); - CloseDialog(); + CloseModalDialog('assembly_references_dialog_id'); } function sendMissingAssemblyReferences(missingAssemblyHashes, continuation) { diff --git a/Cecilifier.Web/wwwroot/js/cecilifier.js b/Cecilifier.Web/wwwroot/js/cecilifier.js index 3f7987fe..c7670262 100644 --- a/Cecilifier.Web/wwwroot/js/cecilifier.js +++ b/Cecilifier.Web/wwwroot/js/cecilifier.js @@ -128,6 +128,19 @@ class Foo setTooltips(cecilifierVersion, frameWorkVersion, buildDate, buildGitRevision); initializeWebSocket(); disableScroll(); + + displayTour(); +} + +function displayTour(force) { + const showTour = getCookie("showTour"); + if (showTour === "false" && (force === false || force === undefined)) + return; + + setCookie("showTour", "false", 1000); + + var intro = introJs.tour(); + intro.start(); } function configureKeyboardShortcuts() { diff --git a/global.json b/global.json deleted file mode 100644 index e23e0211..00000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "9.0.0", - "rollForward": "latestMinor" - } -}