暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

InvokeMember.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace Unity.VisualScripting
  5. {
  6. /// <summary>
  7. /// Invokes a method or a constructor via reflection.
  8. /// </summary>
  9. public sealed class InvokeMember : MemberUnit
  10. {
  11. public InvokeMember() : base() { }
  12. public InvokeMember(Member member) : base(member) { }
  13. private bool useExpandedParameters;
  14. /// <summary>
  15. /// Whether the target should be output to allow for chaining.
  16. /// </summary>
  17. [Serialize]
  18. [InspectableIf(nameof(supportsChaining))]
  19. public bool chainable { get; set; }
  20. [DoNotSerialize]
  21. public bool supportsChaining => member.requiresTarget;
  22. [DoNotSerialize]
  23. [MemberFilter(Methods = true, Constructors = true)]
  24. public Member invocation
  25. {
  26. get { return member; }
  27. set { member = value; }
  28. }
  29. [DoNotSerialize]
  30. [PortLabelHidden]
  31. public ControlInput enter { get; private set; }
  32. [DoNotSerialize]
  33. public Dictionary<int, ValueInput> inputParameters { get; private set; }
  34. /// <summary>
  35. /// The target object used when setting the value.
  36. /// </summary>
  37. [DoNotSerialize]
  38. [PortLabel("Target")]
  39. [PortLabelHidden]
  40. public ValueOutput targetOutput { get; private set; }
  41. [DoNotSerialize]
  42. [PortLabelHidden]
  43. public ValueOutput result { get; private set; }
  44. [DoNotSerialize]
  45. public Dictionary<int, ValueOutput> outputParameters { get; private set; }
  46. [DoNotSerialize]
  47. [PortLabelHidden]
  48. public ControlOutput exit { get; private set; }
  49. [DoNotSerialize]
  50. private int parameterCount;
  51. [Serialize]
  52. List<string> parameterNames;
  53. public override bool HandleDependencies()
  54. {
  55. if (!base.HandleDependencies())
  56. return false;
  57. // Here we have a chance to do a bit of post processing after deserialization of this node has occured.
  58. // In the past we did not serialize parameter names explicitly (only parameter types), however, if we have
  59. // exactly the same number of defaults as parameters, we happen to know what the original parameter names were.
  60. if (parameterNames == null && member.parameterTypes.Length == defaultValues.Count)
  61. {
  62. // Note that we strip the "%" prefix from the parameter name in the default values (the "%" denotes that
  63. // it is a parameter input)
  64. parameterNames = defaultValues.Select(defaultValue => defaultValue.Key.Substring(1)).ToList();
  65. }
  66. return true;
  67. }
  68. protected override void Definition()
  69. {
  70. base.Definition();
  71. inputParameters = new Dictionary<int, ValueInput>();
  72. outputParameters = new Dictionary<int, ValueOutput>();
  73. useExpandedParameters = true;
  74. enter = ControlInput(nameof(enter), Enter);
  75. exit = ControlOutput(nameof(exit));
  76. Succession(enter, exit);
  77. if (member.requiresTarget)
  78. {
  79. Requirement(target, enter);
  80. }
  81. if (supportsChaining && chainable)
  82. {
  83. targetOutput = ValueOutput(member.targetType, nameof(targetOutput));
  84. Assignment(enter, targetOutput);
  85. }
  86. if (member.isGettable)
  87. {
  88. result = ValueOutput(member.type, nameof(result), Result);
  89. if (member.requiresTarget)
  90. {
  91. Requirement(target, result);
  92. }
  93. }
  94. var parameterInfos = member.GetParameterInfos().ToArray();
  95. parameterCount = parameterInfos.Length;
  96. bool needsParameterRemapping = false;
  97. for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
  98. {
  99. var parameterInfo = parameterInfos[parameterIndex];
  100. var parameterType = parameterInfo.UnderlyingParameterType();
  101. if (!parameterInfo.HasOutModifier())
  102. {
  103. var inputParameterKey = "%" + parameterInfo.Name;
  104. // Changes in parameter names are tolerated, use the old parameter naming for now and fix it later.
  105. if (parameterNames != null && parameterNames[parameterIndex] != parameterInfo.Name)
  106. {
  107. inputParameterKey = "%" + parameterNames[parameterIndex];
  108. needsParameterRemapping = true;
  109. }
  110. var inputParameter = ValueInput(parameterType, inputParameterKey);
  111. inputParameters.Add(parameterIndex, inputParameter);
  112. inputParameter.SetDefaultValue(parameterInfo.PseudoDefaultValue());
  113. if (parameterInfo.AllowsNull())
  114. {
  115. inputParameter.AllowsNull();
  116. }
  117. Requirement(inputParameter, enter);
  118. if (member.isGettable)
  119. {
  120. Requirement(inputParameter, result);
  121. }
  122. }
  123. if (parameterInfo.ParameterType.IsByRef || parameterInfo.IsOut)
  124. {
  125. var outputParameterKey = "&" + parameterInfo.Name;
  126. // Changes in parameter names are tolerated, use the old parameter naming for now and fix it later.
  127. if (parameterNames != null && parameterNames[parameterIndex] != parameterInfo.Name)
  128. {
  129. outputParameterKey = "&" + parameterNames[parameterIndex];
  130. needsParameterRemapping = true;
  131. }
  132. var outputParameter = ValueOutput(parameterType, outputParameterKey);
  133. outputParameters.Add(parameterIndex, outputParameter);
  134. Assignment(enter, outputParameter);
  135. useExpandedParameters = false;
  136. }
  137. }
  138. if (inputParameters.Count > 5)
  139. {
  140. useExpandedParameters = false;
  141. }
  142. if (parameterNames == null)
  143. {
  144. parameterNames = parameterInfos.Select(pInfo => pInfo.Name).ToList();
  145. }
  146. if (needsParameterRemapping)
  147. {
  148. // Note, this will have no effect unless we are in an Editor context. This is okay since for runtime
  149. // purposes as it is actually fine to continue to use the old parameter names for the sake of setting up
  150. // connections and default values. The only reason it is interesting to update to the new parameter
  151. // names is for UI purposes.
  152. UnityThread.EditorAsync(PostDeserializeRemapParameterNames);
  153. }
  154. }
  155. private void PostDeserializeRemapParameterNames()
  156. {
  157. var parameterInfos = member.GetParameterInfos().ToArray();
  158. // Sanity check
  159. if (parameterNames?.Count != parameterInfos.Length)
  160. return;
  161. // Check if any of the method parameter names have changed (Note: handling of parameter type changes is not
  162. // supported here, it is detected and handled elsewhere)
  163. List<(ValueInput port, ValueOutput[] connectedSources)> renamedInputs = null;
  164. List<(ValueOutput port, ValueInput[] connectedDestinations)> renamedOutputs = null;
  165. List<(string name, object value)> renamedDefaults = null;
  166. for (var i = 0; i < parameterInfos.Length; ++i)
  167. {
  168. var paramInfo = parameterInfos[i];
  169. var oldParamName = parameterNames[i];
  170. if (paramInfo.Name != oldParamName)
  171. {
  172. // Phase 1 of parameter renaming: disconnect any nodes connected to affected ports, remove affected
  173. // ports from port definition, and remove any default values associated with affected ports.
  174. if (valueInputs.TryGetValue("%" + oldParamName, out var oldInput))
  175. {
  176. var connectionSources = oldInput.validConnections.Select(con => con.source).ToArray();
  177. foreach (var source in connectionSources)
  178. source.DisconnectFromValid(oldInput);
  179. valueInputs.Remove(oldInput);
  180. if (renamedInputs == null)
  181. renamedInputs = new List<(ValueInput, ValueOutput[])>(1);
  182. renamedInputs.Add((new ValueInput("%" + paramInfo.Name, paramInfo.ParameterType), connectionSources));
  183. if (defaultValues.TryGetValue(oldInput.key, out var defaultValue))
  184. {
  185. defaultValues.Remove(oldInput.key);
  186. if (renamedDefaults == null)
  187. renamedDefaults = new List<(string, object)>(1);
  188. renamedDefaults.Add(("%" + paramInfo.Name, defaultValue));
  189. }
  190. }
  191. else if (valueOutputs.TryGetValue("&" + oldParamName, out var oldOutput))
  192. {
  193. var connectionDestinations = oldOutput.validConnections.Select(con => con.destination).ToArray();
  194. foreach (var destination in connectionDestinations)
  195. destination.DisconnectFromValid(oldOutput);
  196. valueOutputs.Remove(oldOutput);
  197. if (renamedOutputs == null)
  198. renamedOutputs = new List<(ValueOutput, ValueInput[])>(1);
  199. renamedOutputs.Add((new ValueOutput("&" + paramInfo.Name, paramInfo.ParameterType), connectionDestinations));
  200. }
  201. parameterNames[i] = paramInfo.Name;
  202. }
  203. }
  204. // Phase 2 of parameter renaming: add renamed version of affected ports back to the port definition, reconnect
  205. // nodes back to those renamed ports, and redefine default values for those ports.
  206. if (renamedInputs != null)
  207. {
  208. foreach (var renamedInput in renamedInputs)
  209. {
  210. valueInputs.Add(renamedInput.port);
  211. foreach (var source in renamedInput.connectedSources)
  212. source.ConnectToValid(renamedInput.port);
  213. }
  214. if (renamedDefaults != null)
  215. {
  216. foreach (var renamedDefault in renamedDefaults)
  217. defaultValues[renamedDefault.name] = renamedDefault.value;
  218. }
  219. }
  220. if (renamedOutputs != null)
  221. {
  222. foreach (var renamedOutput in renamedOutputs)
  223. {
  224. valueOutputs.Add(renamedOutput.port);
  225. foreach (var destination in renamedOutput.connectedDestinations)
  226. destination.ConnectToValid(renamedOutput.port);
  227. }
  228. }
  229. if (renamedInputs != null || renamedOutputs != null)
  230. {
  231. Define();
  232. }
  233. }
  234. protected override bool IsMemberValid(Member member)
  235. {
  236. return member.isInvocable;
  237. }
  238. private object Invoke(object target, Flow flow)
  239. {
  240. if (useExpandedParameters)
  241. {
  242. switch (inputParameters.Count)
  243. {
  244. case 0:
  245. return member.Invoke(target);
  246. case 1:
  247. return member.Invoke(target,
  248. flow.GetConvertedValue(inputParameters[0]));
  249. case 2:
  250. return member.Invoke(target,
  251. flow.GetConvertedValue(inputParameters[0]),
  252. flow.GetConvertedValue(inputParameters[1]));
  253. case 3:
  254. return member.Invoke(target,
  255. flow.GetConvertedValue(inputParameters[0]),
  256. flow.GetConvertedValue(inputParameters[1]),
  257. flow.GetConvertedValue(inputParameters[2]));
  258. case 4:
  259. return member.Invoke(target,
  260. flow.GetConvertedValue(inputParameters[0]),
  261. flow.GetConvertedValue(inputParameters[1]),
  262. flow.GetConvertedValue(inputParameters[2]),
  263. flow.GetConvertedValue(inputParameters[3]));
  264. case 5:
  265. return member.Invoke(target,
  266. flow.GetConvertedValue(inputParameters[0]),
  267. flow.GetConvertedValue(inputParameters[1]),
  268. flow.GetConvertedValue(inputParameters[2]),
  269. flow.GetConvertedValue(inputParameters[3]),
  270. flow.GetConvertedValue(inputParameters[4]));
  271. default:
  272. throw new NotSupportedException();
  273. }
  274. }
  275. else
  276. {
  277. var arguments = new object[parameterCount];
  278. for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
  279. {
  280. if (inputParameters.TryGetValue(parameterIndex, out var inputParameter))
  281. {
  282. arguments[parameterIndex] = flow.GetConvertedValue(inputParameter);
  283. }
  284. }
  285. var result = member.Invoke(target, arguments);
  286. for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
  287. {
  288. if (outputParameters.TryGetValue(parameterIndex, out var outputParameter))
  289. {
  290. flow.SetValue(outputParameter, arguments[parameterIndex]);
  291. }
  292. }
  293. return result;
  294. }
  295. }
  296. private object GetAndChainTarget(Flow flow)
  297. {
  298. if (member.requiresTarget)
  299. {
  300. var target = flow.GetValue(this.target, member.targetType);
  301. if (supportsChaining && chainable)
  302. {
  303. flow.SetValue(targetOutput, target);
  304. }
  305. return target;
  306. }
  307. return null;
  308. }
  309. private object Result(Flow flow)
  310. {
  311. var target = GetAndChainTarget(flow);
  312. return Invoke(target, flow);
  313. }
  314. private ControlOutput Enter(Flow flow)
  315. {
  316. var target = GetAndChainTarget(flow);
  317. var result = Invoke(target, flow);
  318. if (this.result != null)
  319. {
  320. flow.SetValue(this.result, result);
  321. }
  322. return exit;
  323. }
  324. #region Analytics
  325. public override AnalyticsIdentifier GetAnalyticsIdentifier()
  326. {
  327. const int maxNumParameters = 5;
  328. var s = $"{member.targetType.FullName}.{member.name}";
  329. if (member.parameterTypes != null)
  330. {
  331. s += "(";
  332. for (var i = 0; i < member.parameterTypes.Length; ++i)
  333. {
  334. if (i >= maxNumParameters)
  335. {
  336. s += $"->{i}";
  337. break;
  338. }
  339. s += member.parameterTypes[i].FullName;
  340. if (i < member.parameterTypes.Length - 1)
  341. s += ", ";
  342. }
  343. s += ")";
  344. }
  345. var aid = new AnalyticsIdentifier
  346. {
  347. Identifier = s,
  348. Namespace = member.targetType.Namespace
  349. };
  350. aid.Hashcode = aid.Identifier.GetHashCode();
  351. return aid;
  352. }
  353. #endregion
  354. }
  355. }