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