123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace Unity.VisualScripting
- {
- /// <summary>
- /// Invokes a method or a constructor via reflection.
- /// </summary>
- public sealed class InvokeMember : MemberUnit
- {
- public InvokeMember() : base() { }
- public InvokeMember(Member member) : base(member) { }
- private bool useExpandedParameters;
- /// <summary>
- /// Whether the target should be output to allow for chaining.
- /// </summary>
- [Serialize]
- [InspectableIf(nameof(supportsChaining))]
- public bool chainable { get; set; }
- [DoNotSerialize]
- public bool supportsChaining => member.requiresTarget;
- [DoNotSerialize]
- [MemberFilter(Methods = true, Constructors = true)]
- public Member invocation
- {
- get { return member; }
- set { member = value; }
- }
- [DoNotSerialize]
- [PortLabelHidden]
- public ControlInput enter { get; private set; }
- [DoNotSerialize]
- public Dictionary<int, ValueInput> inputParameters { get; private set; }
- /// <summary>
- /// The target object used when setting the value.
- /// </summary>
- [DoNotSerialize]
- [PortLabel("Target")]
- [PortLabelHidden]
- public ValueOutput targetOutput { get; private set; }
- [DoNotSerialize]
- [PortLabelHidden]
- public ValueOutput result { get; private set; }
- [DoNotSerialize]
- public Dictionary<int, ValueOutput> outputParameters { get; private set; }
- [DoNotSerialize]
- [PortLabelHidden]
- public ControlOutput exit { get; private set; }
- [DoNotSerialize]
- private int parameterCount;
- protected override void Definition()
- {
- base.Definition();
- inputParameters = new Dictionary<int, ValueInput>();
- outputParameters = new Dictionary<int, ValueOutput>();
- useExpandedParameters = true;
- enter = ControlInput(nameof(enter), Enter);
- exit = ControlOutput(nameof(exit));
- Succession(enter, exit);
- if (member.requiresTarget)
- {
- Requirement(target, enter);
- }
- if (supportsChaining && chainable)
- {
- targetOutput = ValueOutput(member.targetType, nameof(targetOutput));
- Assignment(enter, targetOutput);
- }
- if (member.isGettable)
- {
- result = ValueOutput(member.type, nameof(result), Result);
- if (member.requiresTarget)
- {
- Requirement(target, result);
- }
- }
- var parameterInfos = member.GetParameterInfos().ToArray();
- parameterCount = parameterInfos.Length;
- for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
- {
- var parameterInfo = parameterInfos[parameterIndex];
- var parameterType = parameterInfo.UnderlyingParameterType();
- if (!parameterInfo.HasOutModifier())
- {
- var inputParameterKey = "%" + parameterInfo.Name;
- var inputParameter = ValueInput(parameterType, inputParameterKey);
- inputParameters.Add(parameterIndex, inputParameter);
- inputParameter.SetDefaultValue(parameterInfo.PseudoDefaultValue());
- if (parameterInfo.AllowsNull())
- {
- inputParameter.AllowsNull();
- }
- Requirement(inputParameter, enter);
- if (member.isGettable)
- {
- Requirement(inputParameter, result);
- }
- }
- if (parameterInfo.ParameterType.IsByRef || parameterInfo.IsOut)
- {
- var outputParameterKey = "&" + parameterInfo.Name;
- var outputParameter = ValueOutput(parameterType, outputParameterKey);
- outputParameters.Add(parameterIndex, outputParameter);
- Assignment(enter, outputParameter);
- useExpandedParameters = false;
- }
- }
- if (inputParameters.Count > 5)
- {
- useExpandedParameters = false;
- }
- }
- protected override bool IsMemberValid(Member member)
- {
- return member.isInvocable;
- }
- private object Invoke(object target, Flow flow)
- {
- if (useExpandedParameters)
- {
- switch (inputParameters.Count)
- {
- case 0:
- return member.Invoke(target);
- case 1:
- return member.Invoke(target,
- flow.GetConvertedValue(inputParameters[0]));
- case 2:
- return member.Invoke(target,
- flow.GetConvertedValue(inputParameters[0]),
- flow.GetConvertedValue(inputParameters[1]));
- case 3:
- return member.Invoke(target,
- flow.GetConvertedValue(inputParameters[0]),
- flow.GetConvertedValue(inputParameters[1]),
- flow.GetConvertedValue(inputParameters[2]));
- case 4:
- return member.Invoke(target,
- flow.GetConvertedValue(inputParameters[0]),
- flow.GetConvertedValue(inputParameters[1]),
- flow.GetConvertedValue(inputParameters[2]),
- flow.GetConvertedValue(inputParameters[3]));
- case 5:
- return member.Invoke(target,
- flow.GetConvertedValue(inputParameters[0]),
- flow.GetConvertedValue(inputParameters[1]),
- flow.GetConvertedValue(inputParameters[2]),
- flow.GetConvertedValue(inputParameters[3]),
- flow.GetConvertedValue(inputParameters[4]));
- default:
- throw new NotSupportedException();
- }
- }
- else
- {
- var arguments = new object[parameterCount];
- for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
- {
- if (inputParameters.TryGetValue(parameterIndex, out var inputParameter))
- {
- arguments[parameterIndex] = flow.GetConvertedValue(inputParameter);
- }
- }
- var result = member.Invoke(target, arguments);
- for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
- {
- if (outputParameters.TryGetValue(parameterIndex, out var outputParameter))
- {
- flow.SetValue(outputParameter, arguments[parameterIndex]);
- }
- }
- return result;
- }
- }
- private object GetAndChainTarget(Flow flow)
- {
- if (member.requiresTarget)
- {
- var target = flow.GetValue(this.target, member.targetType);
- if (supportsChaining && chainable)
- {
- flow.SetValue(targetOutput, target);
- }
- return target;
- }
- return null;
- }
- private object Result(Flow flow)
- {
- var target = GetAndChainTarget(flow);
- return Invoke(target, flow);
- }
- private ControlOutput Enter(Flow flow)
- {
- var target = GetAndChainTarget(flow);
- var result = Invoke(target, flow);
- if (this.result != null)
- {
- flow.SetValue(this.result, result);
- }
- return exit;
- }
- #region Analytics
- public override AnalyticsIdentifier GetAnalyticsIdentifier()
- {
- const int maxNumParameters = 5;
- var s = $"{member.targetType.FullName}.{member.name}";
- if (member.parameterTypes != null)
- {
- s += "(";
- for (var i = 0; i < member.parameterTypes.Length; ++i)
- {
- if (i >= maxNumParameters)
- {
- s += $"->{i}";
- break;
- }
- s += member.parameterTypes[i].FullName;
- if (i < member.parameterTypes.Length - 1)
- s += ", ";
- }
- s += ")";
- }
- var aid = new AnalyticsIdentifier
- {
- Identifier = s,
- Namespace = member.targetType.Namespace
- };
- aid.Hashcode = aid.Identifier.GetHashCode();
- return aid;
- }
- #endregion
- }
- }
|