From c4c627355b703e4f214e1fbe17bad7485614bc44 Mon Sep 17 00:00:00 2001 From: Robin Kahlow Date: Sun, 24 Mar 2019 23:51:56 +0000 Subject: [PATCH] added support for different pass types (ref / out / in) --- NetPrints/Core/MethodSpecifier.cs | 46 +++++++++++++++++-- NetPrints/Core/Named.cs | 1 + NetPrints/Graph/CallMethodNode.cs | 6 +-- NetPrints/Graph/GenericsHelper.cs | 4 +- NetPrints/Graph/GraphUtil.cs | 4 +- NetPrints/Translator/MethodTranslator.cs | 25 +++++++++- .../Controls/MethodEditorControl.xaml.cs | 2 +- .../Converters/MethodSpecifierConverter.cs | 2 +- .../Reflection/ReflectionConverter.cs | 19 ++++++-- NetPrintsUnitTests/ClassTranslatorTests.cs | 4 +- NetPrintsUnitTests/DelegateTranslatorTests.cs | 6 ++- 11 files changed, 98 insertions(+), 21 deletions(-) diff --git a/NetPrints/Core/MethodSpecifier.cs b/NetPrints/Core/MethodSpecifier.cs index 9a397cf..72f463c 100644 --- a/NetPrints/Core/MethodSpecifier.cs +++ b/NetPrints/Core/MethodSpecifier.cs @@ -9,6 +9,31 @@ namespace NetPrints.Core { + public enum MethodParameterPassType + { + Default, + Reference, + Out, + In + } + + [DataContract] + public class MethodParameter : Named + { + [DataMember] + public MethodParameterPassType PassType + { + get; + private set; + } + + public MethodParameter(string name, BaseType type, MethodParameterPassType passType) + : base(name, type) + { + PassType = passType; + } + } + /// /// Specifier describing a method. /// @@ -40,18 +65,29 @@ public TypeSpecifier DeclaringType /// Named specifiers for the types this method takes as arguments. /// [DataMember] - public IList> Arguments + public IList Parameters { get; private set; } + [DataMember] + [Obsolete("Use Parameters instead.")] + public IList> Arguments + { + get => Parameters.Cast>().ToList(); + private set + { + Parameters = value.Select(arg => new MethodParameter(arg.Name, arg.Value, MethodParameterPassType.Default)).ToList(); + } + } + /// /// Specifiers for the types this method takes as arguments. /// public IReadOnlyList ArgumentTypes { - get => Arguments.Select(t => (BaseType)t).ToArray(); + get => Parameters.Select(t => (BaseType)t).ToArray(); } /// @@ -103,13 +139,13 @@ public IList GenericArguments /// Modifiers of the method. /// Specifier for the type this method is contained in. /// Generic arguments this method takes. - public MethodSpecifier(string name, IEnumerable> arguments, + public MethodSpecifier(string name, IEnumerable arguments, IEnumerable returnTypes, MethodModifiers modifiers, MemberVisibility visibility, TypeSpecifier declaringType, IList genericArguments) { Name = name; DeclaringType = declaringType; - Arguments = arguments.ToList(); + Parameters = arguments.ToList(); ReturnTypes = returnTypes.ToList(); Modifiers = modifiers; Visibility = visibility; @@ -127,7 +163,7 @@ public override string ToString() methodString += Name; - string argTypeString = string.Join(", ", Arguments.Select(a => a.Value.ShortName)); + string argTypeString = string.Join(", ", Parameters.Select(a => a.Value.ShortName)); methodString += $"({argTypeString})"; diff --git a/NetPrints/Core/Named.cs b/NetPrints/Core/Named.cs index cede57f..1776376 100644 --- a/NetPrints/Core/Named.cs +++ b/NetPrints/Core/Named.cs @@ -12,6 +12,7 @@ namespace NetPrints.Core /// /// Type of the value. [DataContract] + [KnownType(typeof(MethodParameter))] public class Named { [DataMember] diff --git a/NetPrints/Graph/CallMethodNode.cs b/NetPrints/Graph/CallMethodNode.cs index b3501c0..c3cee1a 100644 --- a/NetPrints/Graph/CallMethodNode.cs +++ b/NetPrints/Graph/CallMethodNode.cs @@ -172,7 +172,7 @@ public IList ReturnValuePins AddExceptionPins(); - foreach (Named argument in MethodSpecifier.Arguments) + foreach (Named argument in MethodSpecifier.Parameters) { AddInputDataPin(argument.Name, argument.Value); } @@ -219,9 +219,9 @@ protected override void OnInputTypeChanged(object sender, EventArgs eventArgs) private void UpdateTypes() { - for (int i = 0; i < MethodSpecifier.Arguments.Count; i++) + for (int i = 0; i < MethodSpecifier.Parameters.Count; i++) { - BaseType type = MethodSpecifier.Arguments[i]; + BaseType type = MethodSpecifier.Parameters[i]; // Construct type with generic arguments replaced by our input type pins BaseType constructedType = GenericsHelper.ConstructWithTypePins(type, InputTypePins); diff --git a/NetPrints/Graph/GenericsHelper.cs b/NetPrints/Graph/GenericsHelper.cs index 9561d8e..b06d840 100644 --- a/NetPrints/Graph/GenericsHelper.cs +++ b/NetPrints/Graph/GenericsHelper.cs @@ -37,7 +37,7 @@ public static BaseType ConstructWithTypePins(BaseType type, IEnumerable arg.Name == inputTypePin.Name); + GenericType typeToReplace = typeSpecifier.GenericArguments.SingleOrDefault(arg => arg.Name == inputTypePin.Name) as GenericType; // If we can not replace all if (!(typeToReplace is null)) @@ -59,7 +59,7 @@ public static BaseType ConstructWithTypePins(BaseType type, IEnumerable t.Name == type.Name).InferredType?.Value; + BaseType replacementType = inputTypePins.SingleOrDefault(t => t.Name == type.Name)?.InferredType?.Value; if (replacementType != null) { return replacementType; diff --git a/NetPrints/Graph/GraphUtil.cs b/NetPrints/Graph/GraphUtil.cs index 63e5797..d802303 100644 --- a/NetPrints/Graph/GraphUtil.cs +++ b/NetPrints/Graph/GraphUtil.cs @@ -432,9 +432,9 @@ public static Method AddOverrideMethod(Class cls, MethodSpecifier methodSpecifie int offsetY = -112; // Add argument pins, their type nodes and connect them - for (int i = 0; i < methodSpecifier.Arguments.Count; i++) + for (int i = 0; i < methodSpecifier.Parameters.Count; i++) { - BaseType argType = methodSpecifier.Arguments[i].Value; + BaseType argType = methodSpecifier.Parameters[i].Value; TypeNode argTypeNode = CreateNestedTypeNode(newMethod, argType, newMethod.EntryNode.PositionX + offsetX, newMethod.EntryNode.PositionY + offsetY * (i+1)); newMethod.EntryNode.AddArgument(); diff --git a/NetPrints/Translator/MethodTranslator.cs b/NetPrints/Translator/MethodTranslator.cs index 8be4ca3..78e87a2 100644 --- a/NetPrints/Translator/MethodTranslator.cs +++ b/NetPrints/Translator/MethodTranslator.cs @@ -537,8 +537,31 @@ public void TranslateCallMethodNode(CallMethodNode node) } } + // Prefix with "out" / "ref" / "in" + string[] argNameArray = argumentNames.ToArray(); + Debug.Assert(argNameArray.Length == node.MethodSpecifier.Parameters.Count); + + for (int i = 0; i < node.MethodSpecifier.Parameters.Count; i++) + { + MethodParameterPassType passType = node.MethodSpecifier.Parameters[i].PassType; + switch (passType) + { + case MethodParameterPassType.Out: + argNameArray[i] = "out " + argNameArray[i]; + break; + case MethodParameterPassType.Reference: + argNameArray[i] = "ref " + argNameArray[i]; + break; + case MethodParameterPassType.In: + argNameArray[i] = "in " + argNameArray[i]; + break; + default: + break; + } + } + // Write the method call - builder.AppendLine($"{node.BoundMethodName}({string.Join(", ", argumentNames)});"); + builder.AppendLine($"{node.BoundMethodName}({string.Join(", ", argNameArray)});"); } // Assign the real variables from the temporary tuple diff --git a/NetPrintsEditor/Controls/MethodEditorControl.xaml.cs b/NetPrintsEditor/Controls/MethodEditorControl.xaml.cs index a9de58c..929535b 100644 --- a/NetPrintsEditor/Controls/MethodEditorControl.xaml.cs +++ b/NetPrintsEditor/Controls/MethodEditorControl.xaml.cs @@ -319,7 +319,7 @@ private void OnGridDrop(object sender, DragEventArgs e) // TODO: Get this from method directly somehow // TODO: Get named type specifiers from method MethodSpecifier methodSpecifier = new MethodSpecifier(method.Name, - method.ArgumentTypes.Select(t => new Named("TODO", t)), + method.ArgumentTypes.Select(t => new MethodParameter("TODO", t, MethodParameterPassType.Default)), method.ReturnTypes.Cast(), method.Modifiers, method.Visibility, method.Class.Type, Array.Empty()); diff --git a/NetPrintsEditor/Converters/MethodSpecifierConverter.cs b/NetPrintsEditor/Converters/MethodSpecifierConverter.cs index 752b30b..fc51174 100644 --- a/NetPrintsEditor/Converters/MethodSpecifierConverter.cs +++ b/NetPrintsEditor/Converters/MethodSpecifierConverter.cs @@ -25,7 +25,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn name = methodSpecifier.Name; } - string paramTypeNames = string.Join(", ", methodSpecifier.Arguments); + string paramTypeNames = string.Join(", ", methodSpecifier.Parameters); string s = $"{methodSpecifier.DeclaringType} {name} ({paramTypeNames})"; if(methodSpecifier.ReturnTypes.Count > 0) diff --git a/NetPrintsEditor/Reflection/ReflectionConverter.cs b/NetPrintsEditor/Reflection/ReflectionConverter.cs index 4c94516..d81a764 100644 --- a/NetPrintsEditor/Reflection/ReflectionConverter.cs +++ b/NetPrintsEditor/Reflection/ReflectionConverter.cs @@ -100,6 +100,19 @@ public static Named NamedBaseTypeSpecifierFromSymbol(IParameterSymbol return new Named(paramSymbol.Name, BaseTypeSpecifierFromSymbol(paramSymbol.Type)); } + private static readonly Dictionary refKindToPassType = new Dictionary() + { + [RefKind.None] = MethodParameterPassType.Default, + [RefKind.Ref] = MethodParameterPassType.Reference, + [RefKind.Out] = MethodParameterPassType.Out, + [RefKind.In] = MethodParameterPassType.In, + }; + + public static MethodParameter MethodParameterFromSymbol(in IParameterSymbol paramSymbol) + { + return new MethodParameter(paramSymbol.Name, BaseTypeSpecifierFromSymbol(paramSymbol.Type), refKindToPassType[paramSymbol.RefKind]); + } + public static MethodSpecifier MethodSpecifierFromSymbol(IMethodSymbol method) { MemberVisibility visibility = roslynToNetprintsVisibility[method.DeclaredAccessibility]; @@ -137,15 +150,15 @@ public static MethodSpecifier MethodSpecifierFromSymbol(IMethodSymbol method) new BaseType[] { } : new BaseType[] { BaseTypeSpecifierFromSymbol(method.ReturnType) }; - Named[] parameterTypes = method.Parameters.Select( - p => NamedBaseTypeSpecifierFromSymbol(p)).ToArray(); + MethodParameter[] parameters = method.Parameters.Select( + p => MethodParameterFromSymbol(p)).ToArray(); BaseType[] genericArgs = method.TypeParameters.Select( p => BaseTypeSpecifierFromSymbol(p)).ToArray(); return new MethodSpecifier( method.Name, - parameterTypes, + parameters, returnTypes, modifiers, visibility, diff --git a/NetPrintsUnitTests/ClassTranslatorTests.cs b/NetPrintsUnitTests/ClassTranslatorTests.cs index c84a4fe..6baef17 100644 --- a/NetPrintsUnitTests/ClassTranslatorTests.cs +++ b/NetPrintsUnitTests/ClassTranslatorTests.cs @@ -57,11 +57,11 @@ public void CreateMainMethod() Modifiers = MethodModifiers.Static }; - MethodSpecifier stringLengthSpecifier = new MethodSpecifier("StringLength", new Named[0], new List() { TypeSpecifier.FromType() }, + MethodSpecifier stringLengthSpecifier = new MethodSpecifier("StringLength", new MethodParameter[0], new List() { TypeSpecifier.FromType() }, MethodModifiers.None, MemberVisibility.Public, TypeSpecifier.FromType(), Array.Empty()); //MethodSpecifier writeConsoleSpecifier = typeof(Console).GetMethods().Single(m => m.Name == "WriteLine" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(string)); TypeSpecifier stringType = TypeSpecifier.FromType(); - MethodSpecifier writeConsoleSpecifier = new MethodSpecifier("WriteLine", new Named[] { new Named("argName", stringType) }, new BaseType[0], + MethodSpecifier writeConsoleSpecifier = new MethodSpecifier("WriteLine", new MethodParameter[] { new MethodParameter("argName", stringType, MethodParameterPassType.Default) }, new BaseType[0], MethodModifiers.None, MemberVisibility.Public, TypeSpecifier.FromType(typeof(Console)), new BaseType[0]); // Create nodes diff --git a/NetPrintsUnitTests/DelegateTranslatorTests.cs b/NetPrintsUnitTests/DelegateTranslatorTests.cs index f9ae5c3..6849a29 100644 --- a/NetPrintsUnitTests/DelegateTranslatorTests.cs +++ b/NetPrintsUnitTests/DelegateTranslatorTests.cs @@ -40,7 +40,11 @@ public void TestDelegate() } MethodSpecifier delegateMethodSpecifier = new MethodSpecifier("TestMethod", - new Named[] { new Named("arg1", TypeSpecifier.FromType()), new Named("arg2", TypeSpecifier.FromType()) }, + new MethodParameter[] + { + new MethodParameter("arg1", TypeSpecifier.FromType(), MethodParameterPassType.Default), + new MethodParameter("arg2", TypeSpecifier.FromType(), MethodParameterPassType.Default) + }, new BaseType[] { TypeSpecifier.FromType() }, MethodModifiers.Static, MemberVisibility.Public, TypeSpecifier.FromType(), Array.Empty());