Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
NamedMethodCall.cs
1using System;
2using System.Reflection;
3using System.Collections.Generic;
8using System.Threading.Tasks;
11
13{
18 {
19 private readonly string name;
20 private readonly ScriptNode[] parameters;
21 private readonly int nrParameters;
22
36 {
37 this.name = Name;
38
39 this.parameters = Parameters;
40 this.parameters?.SetParent(this);
41
42 this.nrParameters = Parameters.Length;
43 this.isAsync = true;
44 }
45
49 public string Name => this.name;
50
54 public ScriptNode[] Parameters => this.parameters;
55
60 public override bool IsAsynchronous => true;
61
69 {
70 return this.EvaluateAsync(Operand, Variables).Result;
71 }
72
79 public override async Task<IElement> EvaluateAsync(IElement Operand, Variables Variables)
80 {
81 object Object = Operand.AssociatedObjectValue;
82 Type T;
83 IElement[] Arguments;
84 object Instance;
85 int i;
86
87 if (Object is null && this.nullCheck)
88 return ObjectValue.Null;
89
90 T = Object as Type;
91 if (T is null)
92 {
93 T = Object?.GetType() ?? typeof(object);
94 Instance = Object;
95 }
96 else
97 Instance = null;
98
99 Arguments = new IElement[this.nrParameters];
100 for (i = 0; i < this.nrParameters; i++)
101 Arguments[i] = await this.parameters[i].EvaluateAsync(Variables);
102
103 IElement Result = await this.EvaluateAsync(T, Instance, Arguments, Variables);
104
105 if (Result is null)
106 {
107 if (Operand.IsScalar)
108 throw new ScriptRuntimeException("Invalid number or type of parameters.", this);
109
110 LinkedList<IElement> Elements = new LinkedList<IElement>();
111
112 foreach (IElement Item in Operand.ChildElements)
113 Elements.AddLast(await this.EvaluateAsync(Item, Variables));
114
115 return Operand.Encapsulate(Elements, this);
116 }
117
118 return Result;
119 }
120
129 public async Task<IElement> EvaluateAsync(Type T, object Instance, IElement[] Arguments, Variables Variables)
130 {
131 Type PT;
132 object[] ParameterValues;
133 bool[] Extend;
134 object Value;
135 bool DoExtend = false;
136 int i;
137
138 lock (this.synchObject)
139 {
140 if (this.lastType != T)
141 {
142 this.method = null;
143 this.methodType = MethodType.Method;
144 this.methods = null;
145 this.byReference = null;
146 this.lastType = T;
147 }
148
149 if (!(this.method is null) && this.methodType == MethodType.Method)
150 {
151 if (this.methodParametersTypes.Length != this.nrParameters)
152 {
153 this.method = null;
154 this.methodType = MethodType.Method;
155 this.methods = null;
156 this.byReference = null;
157 }
158 else
159 {
160 for (i = 0; i < this.methodParametersTypes.Length; i++)
161 {
162 PT = this.methodParametersTypes[i].ParameterType;
163
164 if (PT.IsByRef && Arguments[i].TryConvertTo(PT.GetElementType(), out Value))
165 {
166 this.methodArgumentExtensions[i] = false;
167 this.methodArguments[i] = Value;
168 }
169 else if (Arguments[i].TryConvertTo(PT, out Value))
170 {
171 this.methodArgumentExtensions[i] = false;
172 this.methodArguments[i] = Value;
173 }
174 else
175 {
176 if (Arguments[i].IsScalar || Variables is null)
177 break;
178
179 this.methodArgumentExtensions[i] = true;
180 this.methodArguments[i] = null;
181 DoExtend = true;
182 }
183 }
184
185 if (i < this.methodParametersTypes.Length)
186 {
187 this.method = null;
188 this.methodType = MethodType.Method;
189 this.methods = null;
190 this.byReference = null;
191 }
192 }
193 }
194
195 if (this.method is null)
196 {
197 if (this.methods is null)
198 this.methods = this.GetMethods(T);
199
200 List<KeyValuePair<string, int>> ByRef = null;
201 ParameterValues = null;
202 Extend = null;
203
204 foreach (MethodRec Rec in this.methods)
205 {
206 DoExtend = false;
207
208 if (Instance is null)
209 {
210 if (!Rec.Method.IsStatic)
211 continue;
212 }
213 else
214 {
215 if (Rec.Method.IsStatic)
216 continue;
217 }
218
219 if (Rec.MethodType == MethodType.Method)
220 {
221 for (i = 0; i < this.nrParameters; i++)
222 {
223 PT = Rec.Parameters[i].ParameterType;
224 IElement Argument = Arguments[i];
225
226 if (PT.IsByRef && ((Value = Argument.AssociatedObjectValue) is null ||
227 Argument.TryConvertTo(PT.GetElementType(), out Value)))
228 {
229 if (ParameterValues is null)
230 {
231 Extend = new bool[this.nrParameters];
232 ParameterValues = new object[this.nrParameters];
233 }
234
235 Extend[i] = false;
236 ParameterValues[i] = Value;
237
238 if (ByRef is null)
239 ByRef = new List<KeyValuePair<string, int>>();
240
241 if (this.parameters[i] is VariableReference Ref)
242 ByRef.Add(new KeyValuePair<string, int>(Ref.VariableName, i));
243 else
244 ByRef.Add(new KeyValuePair<string, int>(null, i));
245 }
246 else if (Argument.TryConvertTo(PT, out Value))
247 {
248 if (ParameterValues is null)
249 {
250 Extend = new bool[this.nrParameters];
251 ParameterValues = new object[this.nrParameters];
252 }
253
254 Extend[i] = false;
255 ParameterValues[i] = Value;
256 }
257 else
258 {
259 if (Argument.IsScalar || Variables is null)
260 break;
261
262 if (Extend is null)
263 {
264 Extend = new bool[this.nrParameters];
265 ParameterValues = new object[this.nrParameters];
266 }
267
268 Extend[i] = true;
269 ParameterValues[i] = null;
270 DoExtend = true;
271 }
272 }
273
274 if (i < this.nrParameters)
275 {
276 ByRef?.Clear();
277 continue;
278 }
279 }
280
281 this.method = Rec.Method;
282 this.methodType = Rec.MethodType;
283 this.methodParametersTypes = Rec.Parameters;
284 this.methodArguments = ParameterValues;
285 this.methodArgumentExtensions = Extend;
286
287 if (!(ByRef is null) && ByRef.Count > 0)
288 this.byReference = ByRef.ToArray();
289 else
290 this.byReference = null;
291
292 break;
293 }
294
295 if (this.method is null)
296 return null;
297 }
298 }
299
300 if (DoExtend)
301 {
302 if (!(this.byReference is null))
303 throw new ScriptException("Canonical extensions of method calls having reference type arguments not supported."); // TODO
304
305 return await this.EvaluateCanonicalAsync(Instance, this.method, this.methodType, this.methodParametersTypes,
306 Arguments, this.methodArguments, this.methodArgumentExtensions, Variables);
307 }
308 else
309 {
310 Value = await this.EvaluateAsync(Instance, this.method, this.methodType, Arguments, this.methodArguments, Variables);
311 return Expression.Encapsulate(Value);
312 }
313 }
314
315 private async Task<IElement> EvaluateAsync(object Instance, MethodInfo Method, MethodType MethodType,
316 IElement[] Arguments, object[] ArgumentValues, Variables Variables)
317 {
318 object Value;
319
320 switch (MethodType)
321 {
322 case MethodType.Method:
323 default:
324 Value = Method.Invoke(Instance, ArgumentValues);
325 Value = await WaitPossibleTask(Value);
326
327 if (!(this.byReference is null))
328 {
329 int i, j, c = this.byReference.Length;
330 string s;
331
332 for (i = 0; i < c; i++)
333 {
334 j = this.byReference[i].Value;
335 if (string.IsNullOrEmpty(s = this.byReference[i].Key))
336 Operators.PatternMatch.Match(this.parameters[j], Expression.Encapsulate(this.methodArguments[j]), Variables, this);
337 else
338 Variables[s] = this.methodArguments[j];
339 }
340 }
341
342 break;
343
344 case MethodType.LambdaProperty:
345 Value = Method.Invoke(Instance, Types.NoParameters);
346 Value = await WaitPossibleTask(Value);
347
348 if (!(Value is ILambdaExpression LambdaExpression))
349 throw new ScriptRuntimeException("Lambda expression property expected.", this);
350
351 Value = await LambdaExpression.EvaluateAsync(Arguments, Variables);
352 break;
353
354 case MethodType.LambdaIndexProperty:
355 Value = Method.Invoke(Instance, new object[] { this.name });
356 Value = await WaitPossibleTask(Value);
357
358 LambdaExpression = Value as ILambdaExpression;
359 if (LambdaExpression is null)
360 throw new ScriptRuntimeException("Lambda expression property expected.", this);
361
362 Value = await LambdaExpression.EvaluateAsync(Arguments, Variables);
363 break;
364 }
365
366 return Expression.Encapsulate(Value);
367 }
368
369 private async Task<IElement> EvaluateCanonicalAsync(object Object, MethodInfo Method, MethodType MethodType,
370 ParameterInfo[] ParametersTypes, IElement[] Arguments, object[] ArgumentValues, bool[] Extend, Variables Variables)
371 {
372 IEnumerator<IElement>[] Enumerators = null;
373 ICollection<IElement> Children;
374 IEnumerator<IElement> e;
375 IElement First = null;
376 int i, c = 0;
377
378 for (i = 0; i < this.nrParameters; i++)
379 {
380 if (Extend[i])
381 {
382 if (Arguments[i].IsScalar)
383 {
384 if (!Arguments[i].TryConvertTo(ParametersTypes[i].ParameterType, out ArgumentValues[i]))
385 throw new ScriptRuntimeException("Inconsistent argument types.", this);
386 }
387 else
388 {
389 Children = Arguments[i].ChildElements;
390 if (First is null)
391 {
392 Enumerators = new IEnumerator<IElement>[this.nrParameters];
393 First = Arguments[i];
394 c = Children.Count;
395 }
396 else if (c != Children.Count)
397 throw new ScriptRuntimeException("Incompatible dimensions.", this);
398
399 Enumerators[i] = Children.GetEnumerator();
400 }
401 }
402 }
403
404 if (First is null)
405 {
406 object Value = await this.EvaluateAsync(Object, Method, MethodType, Arguments, ArgumentValues, Variables);
407 return Expression.Encapsulate(Value);
408 }
409
410 LinkedList<IElement> Elements = new LinkedList<IElement>();
411 Arguments = (IElement[])Arguments.Clone();
412
413 while (true)
414 {
415 for (i = 0; i < this.nrParameters; i++)
416 {
417 if (!Extend[i])
418 continue;
419
420 if (!(e = Enumerators[i]).MoveNext())
421 break;
422
423 Arguments[i] = e.Current;
424 }
425
426 if (i < this.nrParameters)
427 break;
428
429 Elements.AddLast(await this.EvaluateCanonicalAsync(Object, Method, MethodType, ParametersTypes, Arguments,
430 ArgumentValues, Extend, Variables));
431 }
432
433 for (i = 0; i < this.nrParameters; i++)
434 {
435 if (Extend[i])
436 Enumerators[i].Dispose();
437 }
438
439 return First.Encapsulate(Elements, this);
440 }
441
442 private MethodRec[] GetMethods(Type Type)
443 {
444 List<MethodRec> Result = new List<MethodRec>();
445 ParameterInfo[] ParameterInfo;
446 IEnumerable<MethodInfo> Methods = Type.GetRuntimeMethods();
447
448 foreach (MethodInfo MI in Methods)
449 {
450 if (MI.IsAbstract || !MI.IsPublic || MI.Name != this.name)
451 continue;
452
453 ParameterInfo = MI.GetParameters();
454 if (ParameterInfo.Length != this.nrParameters)
455 continue;
456
457 Result.Add(new MethodRec()
458 {
459 Method = MI,
460 Parameters = ParameterInfo,
461 MethodType = MethodType.Method
462 });
463 }
464
465 if (Result.Count == 0)
466 {
467 PropertyInfo PI = Type.GetRuntimeProperty(this.name);
468 if (!(PI is null) && PI.GetIndexParameters().Length == 0)
469 {
470 if (!PI.CanRead)
471 throw new ScriptRuntimeException("Property cannot be read: " + this.name, this);
472 else if (!PI.GetMethod.IsPublic)
473 throw new ScriptRuntimeException("Property not accessible: " + this.name, this);
474 else
475 {
476 Result.Add(new MethodRec()
477 {
478 Method = PI.GetMethod,
479 Parameters = new ParameterInfo[0],
480 MethodType = MethodType.LambdaProperty
481 });
482 }
483 }
484 else if (VectorIndex.TryGetIndexProperty(Type, true, false, out PI, out ParameterInfo[] Parameters))
485 {
486 Result.Add(new MethodRec()
487 {
488 Method = PI.GetMethod,
490 MethodType = MethodType.LambdaIndexProperty
491 });
492 }
493 }
494
495 return Result.ToArray();
496 }
497
498 private enum MethodType
499 {
500 Method,
501 LambdaProperty,
502 LambdaIndexProperty
503 }
504
505 private class MethodRec
506 {
507 public MethodInfo Method;
508 public ParameterInfo[] Parameters;
509 public MethodType MethodType;
510 }
511
512 private Type lastType = null;
513 private MethodInfo method = null;
514 private MethodType methodType = MethodType.Method;
515 private ParameterInfo[] methodParametersTypes = null;
516 private MethodRec[] methods = null;
517 private KeyValuePair<string, int>[] byReference = null;
518 private object[] methodArguments = null;
519 private bool[] methodArgumentExtensions = null;
520 private readonly object synchObject = new object();
521
529 public override bool ForAllChildNodes(ScriptNodeEventHandler Callback, object State, SearchMethod Order)
530 {
531 int i;
532
533 if (Order == SearchMethod.DepthFirst)
534 {
535 if (!this.parameters.ForAllChildNodes(Callback, State, Order))
536 return false;
537 }
538
539 ScriptNode Node;
540
541 for (i = 0; i < this.nrParameters; i++)
542 {
543 Node = this.parameters[i];
544 if (!(Node is null))
545 {
546 bool b = !Callback(Node, out ScriptNode NewNode, State);
547 if (!(NewNode is null))
548 {
549 this.parameters[i] = Node = NewNode;
550 NewNode.SetParent(this);
551 }
552
553 if (b || (Order == SearchMethod.TreeOrder && !Node.ForAllChildNodes(Callback, State, Order)))
554 return false;
555 }
556 }
557
558 if (Order == SearchMethod.BreadthFirst)
559 {
560 if (!this.parameters.ForAllChildNodes(Callback, State, Order))
561 return false;
562 }
563
564 return true;
565 }
566
568 public override bool Equals(object obj)
569 {
570 return obj is NamedMethodCall O &&
571 this.name == O.name &&
572 AreEqual(this.parameters, O.parameters) &&
573 base.Equals(obj);
574 }
575
577 public override int GetHashCode()
578 {
579 int Result = base.GetHashCode();
580 Result ^= Result << 5 ^ GetHashCode(this.name);
581 Result ^= Result << 5 ^ GetHashCode(this.parameters);
582 return Result;
583 }
584 }
585}
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:548
Base class for script exceptions.
Class managing a script expression.
Definition: Expression.cs:39
static IElement Encapsulate(object Value)
Encapsulates an object.
Definition: Expression.cs:4955
Base class for all unary operators performing operand null checks.
bool NullCheck
If null check is to be used.
readonly bool nullCheck
If null should be returned if operand is null.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
bool ForAllChildNodes(ScriptNodeEventHandler Callback, object State, bool DepthFirst)
Calls the callback method for all child nodes.
Definition: ScriptNode.cs:243
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
static bool AreEqual(ScriptNode S1, ScriptNode S2)
Compares if two script nodes are equal.
Definition: ScriptNode.cs:275
static async Task< object > WaitPossibleTask(object Result)
Waits for any asynchronous process to terminate.
Definition: ScriptNode.cs:417
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
void SetParent(ScriptNode Parent)
Sets the parent node. Can only be used when expression is being parsed.
Definition: ScriptNode.cs:132
Represents a variable reference.
static readonly ObjectValue Null
Null value.
Definition: ObjectValue.cs:86
override bool ForAllChildNodes(ScriptNodeEventHandler Callback, object State, SearchMethod Order)
Calls the callback method for all child nodes.
override bool IsAsynchronous
If the node (or its decendants) include asynchronous evaluation. Asynchronous nodes should be evaluat...
override async Task< IElement > EvaluateAsync(IElement Operand, Variables Variables)
Evaluates the node, using the variables provided in the Variables collection.
override IElement Evaluate(IElement Operand, Variables Variables)
Evaluates the node, using the variables provided in the Variables collection.
async Task< IElement > EvaluateAsync(Type T, object Instance, IElement[] Arguments, Variables Variables)
Executes a code-behind method.
NamedMethodCall(ScriptNode Operand, string Name, ScriptNode[] Parameters, bool NullCheck, int Start, int Length, Expression Expression)
Named method call operator.
static bool TryGetIndexProperty(Type T, bool ForReading, bool ForWriting, out PropertyInfo PropertyInfo, out ParameterInfo[] Parameters)
Tries to get a one-dimensional index property of a Type.
Definition: VectorIndex.cs:133
Collection of variables.
Definition: Variables.cs:25
Basic interface for all types of elements.
Definition: IElement.cs:20
bool TryConvertTo(Type DesiredType, out object Value)
Converts the value to a .NET type.
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
ICollection< IElement > ChildElements
An enumeration of child elements. If the element is a scalar, this property will return null.
Definition: IElement.cs:49
IElement Encapsulate(ICollection< IElement > Elements, ScriptNode Node)
Encapsulates a set of elements into a similar structure as that provided by the current element.
bool IsScalar
If the element represents a scalar value.
Definition: IElement.cs:41
Base interface for lambda expressions.
delegate bool ScriptNodeEventHandler(ScriptNode Node, out ScriptNode NewNode, object State)
Delegate for ScriptNode callback methods.
SearchMethod
Method to traverse the expression structure
Definition: ScriptNode.cs:38