Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Callback.cs
1using Microsoft.CodeAnalysis.Emit;
2using System;
3using System.Collections.Generic;
4using System.IO;
5using System.Reflection;
6using System.Text;
7using System.Threading.Tasks;
13
15{
20 {
29 public Callback(ScriptNode DelegateType, ScriptNode Lambda, int Start, int Length, Expression Expression)
30 : base(new ScriptNode[] { DelegateType, Lambda }, argumentTypes2Scalar, Start, Length, Expression)
31 {
32 }
33
44 : base(new ScriptNode[] { DelegateType, ArgumentType, Lambda }, argumentTypes3Scalar, Start, Length, Expression)
45 {
46 }
47
51 public override string FunctionName => nameof(Callback);
52
56 public override string[] DefaultArgumentNames => new string[] { "DelegateType", "Lambda" };
57
65 {
66 int i = 0;
67 int c = Arguments.Length;
68
69 if (!(Arguments[i++].AssociatedObjectValue is Type Type))
70 throw new ScriptRuntimeException("Expected a type in the first argument.", this);
71
72 Dictionary<string, bool> Dependencies = new Dictionary<string, bool>()
73 {
74 { GetLocation(typeof(object)), true },
75 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(object))), "System.Runtime.dll"), true },
76 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(Encoding))), "System.Text.Encoding.dll"), true },
77 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(MemoryStream))), "System.IO.dll"), true },
78 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(MemoryStream))), "System.Runtime.Extensions.dll"), true },
79 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(Task))), "System.Threading.Tasks.dll"), true },
80 { Path.Combine(Path.GetDirectoryName(GetLocation(typeof(Dictionary<string, object>))), "System.Collections.dll"), true },
81 { GetLocation(typeof(Types)), true },
82 { GetLocation(typeof(Expression)), true },
83 { GetLocation(typeof(Callback)), true }
84 };
85
86 Dependencies[GetLocation(Type)] = true;
87
88 if (c > 2)
89 {
90 if (!(Arguments[i++].AssociatedObjectValue is Type TArg1))
91 throw new ScriptRuntimeException("Expected a type in the second argument.", this);
92
93
94 Dependencies[GetLocation(TArg1)] = true;
95
96 Type = Type.MakeGenericType(TArg1);
97 }
98
99 if (!delegateTypeInfo.IsAssignableFrom(Type.GetTypeInfo()))
100 throw new ScriptRuntimeException("Type must be a delegate type.", this);
101
102 if (!(Arguments[i++].AssociatedObjectValue is ILambdaExpression Lambda))
103 throw new ScriptRuntimeException("Expected a lambda expression in the second argument.", this);
104
105 Type ScriptProxyType;
106
107 lock (scriptProxyTypes)
108 {
109 if (!scriptProxyTypes.TryGetValue(Type, out ScriptProxyType))
110 {
111 Type ReturnType = null;
112 ParameterInfo[] Parameters = null;
113
114 foreach (MethodInfo MI in Type.GetRuntimeMethods())
115 {
116 if (MI.Name == "Invoke")
117 {
118 ReturnType = MI.ReturnType;
119 Parameters = MI.GetParameters();
120 break;
121 }
122 }
123
124 if (ReturnType is null || Parameters is null)
125 throw new ScriptRuntimeException("Delegate type lacks an Invoke method.", this);
126
127 bool IsAsync = taskTypeInfo.IsAssignableFrom(ReturnType.GetTypeInfo());
128
129 string ClassTypeName = Type.Name.Replace("`", "_GT_");
130 string ReferenceTypeName = ClassTypeName;
131 Type[] TypeArguments = Type.IsConstructedGenericType ? Type.GenericTypeArguments : null;
132 int j, d = TypeArguments?.Length ?? 0;
133 StringBuilder sb = new StringBuilder();
134 string s;
135
136 for (j = 0; j < d; j++)
137 ClassTypeName = ClassTypeName.Replace("GT_" + (j + 1).ToString(), TypeArguments[j].FullName.Replace(".", "_"));
138
139 StringBuilder CSharp = new StringBuilder();
140
141 CSharp.AppendLine("using System;");
142 CSharp.AppendLine("using Waher.Script;");
143 CSharp.AppendLine("using Waher.Script.Abstraction.Elements;");
144 CSharp.AppendLine("using Waher.Script.Data.Functions;");
145 CSharp.AppendLine("using Waher.Script.Model;");
146 CSharp.AppendLine();
147 CSharp.Append("namespace ");
148 CSharp.Append(Type.Namespace);
149 CSharp.AppendLine(".ScriptCallbacks");
150 CSharp.AppendLine("{");
151 CSharp.Append("\tpublic class ScriptProxy");
152 CSharp.Append(ClassTypeName);
153 CSharp.Append(" : ScriptProxy<");
154
155 if (d > 0)
156 {
157 Type GenericType = Type.GetGenericTypeDefinition();
158 s = GenericType.FullName;
159
160 j = s.IndexOf('`');
161 if (j > 0)
162 s = s.Substring(0, j);
163
164 sb.Append(s);
165 sb.Append('<');
166
167 for (j = 0; j < d; j++)
168 {
169 if (j > 0)
170 sb.Append(", ");
171
172 sb.Append(TypeArguments[j].FullName);
173 }
174
175 sb.Append('>');
176
177 ReferenceTypeName = sb.ToString();
178 CSharp.Append(ReferenceTypeName);
179 sb.Clear();
180 }
181 else
182 CSharp.Append(Type.FullName);
183
184 CSharp.AppendLine(">");
185 CSharp.AppendLine("\t{");
186 CSharp.Append("\t\tpublic ScriptProxy");
187 CSharp.Append(ClassTypeName);
188 CSharp.AppendLine("(ILambdaExpression Lambda, Variables Variables)");
189 CSharp.AppendLine("\t\t\t: base(Lambda, Variables)");
190 CSharp.AppendLine("\t\t{");
191 CSharp.AppendLine("\t\t}");
192 CSharp.AppendLine();
193 CSharp.Append("\t\tpublic override ");
194 CSharp.Append(ReferenceTypeName);
195 CSharp.AppendLine(" GetCallbackFunction()");
196 CSharp.AppendLine("\t\t{");
197 CSharp.AppendLine("\t\t\treturn this.CallLambda;");
198 CSharp.AppendLine("\t\t}");
199 CSharp.AppendLine();
200 CSharp.Append("\t\tprivate ");
201
202 if (ReturnType == typeof(void))
203 CSharp.Append("void");
204 else
205 {
206 if (IsAsync)
207 CSharp.Append("async ");
208
209 AppendType(ReturnType, CSharp);
210
211 if (IsAsync)
212 {
213 if (ReturnType.IsConstructedGenericType)
214 ReturnType = ReturnType.GenericTypeArguments[0];
215 else
216 ReturnType = typeof(void);
217 }
218 }
219
220 CSharp.Append(" CallLambda(");
221
222 bool First = true;
223
224 foreach (ParameterInfo Parameter in Parameters)
225 {
226 if (First)
227 First = false;
228 else
229 CSharp.Append(", ");
230
231 AppendType(Parameter.ParameterType, CSharp);
232 CSharp.Append(' ');
233 CSharp.Append(Parameter.Name);
234 }
235
236 CSharp.AppendLine(")");
237 CSharp.AppendLine("\t\t{");
238
239 CSharp.Append("\t\t\t");
240
241 if (ReturnType != typeof(void))
242 CSharp.Append("IElement Result = ");
243
244 if (IsAsync)
245 CSharp.Append("await this.Lambda.EvaluateAsync(");
246 else
247 CSharp.Append("this.Lambda.Evaluate(");
248
249 if (Parameters.Length == 0)
250 CSharp.Append("new IElement[0]");
251 else
252 {
253 CSharp.AppendLine("new IElement[]");
254 CSharp.AppendLine("\t\t\t{");
255
256 First = true;
257
258 foreach (ParameterInfo Parameter in Parameters)
259 {
260 if (First)
261 First = false;
262 else
263 CSharp.AppendLine(",");
264
265 CSharp.Append("\t\t\t\tExpression.Encapsulate(");
266 CSharp.Append(Parameter.Name);
267 CSharp.Append(')');
268 }
269
270 CSharp.AppendLine();
271 CSharp.Append("\t\t\t}");
272 }
273
274 CSharp.AppendLine(", this.Variables);");
275 CSharp.AppendLine();
276
277 if (ReturnType != typeof(void))
278 {
279 if (ReturnType == typeof(IElement))
280 CSharp.AppendLine("\t\t\treturn Result;");
281 else
282 {
283 CSharp.Append("\t\t\treturn (");
284 AppendType(ReturnType, CSharp);
285 CSharp.AppendLine(")Result.AssociatedObjectValue;");
286 }
287 }
288
289 CSharp.AppendLine("\t\t}");
290 CSharp.AppendLine("\t}");
291 CSharp.AppendLine("}");
292
293 string CSharpCode = CSharp.ToString();
294
295 TypeInfo LoopInfo;
296 Type Loop = Type;
297 PropertyInfo PI;
298 FieldInfo FI;
299 s = Path.Combine(Path.GetDirectoryName(GetLocation(typeof(object))), "netstandard.dll");
300
301 if (File.Exists(s))
302 Dependencies[s] = true;
303
304 while (!(Loop is null))
305 {
306 LoopInfo = Loop.GetTypeInfo();
307 Dependencies[GetLocation(Loop)] = true;
308
309 foreach (Type Interface in LoopInfo.ImplementedInterfaces)
310 {
311 s = GetLocation(Interface);
312 Dependencies[s] = true;
313 }
314
315 foreach (MemberInfo MI2 in LoopInfo.DeclaredMembers)
316 {
317 FI = MI2 as FieldInfo;
318 if (!(FI is null) && !((s = GetLocation(FI.FieldType)).EndsWith("mscorlib.dll") || s.EndsWith("System.Runtime.dll") || s.EndsWith("System.Private.CoreLib.dll")))
319 Dependencies[s] = true;
320 PI = MI2 as PropertyInfo;
321 if (!(PI is null) && !((s = GetLocation(PI.PropertyType)).EndsWith("mscorlib.dll") || s.EndsWith("System.Runtime.dll") || s.EndsWith("System.Private.CoreLib.dll")))
322 Dependencies[s] = true;
323 }
324 Loop = LoopInfo.BaseType;
325 if (Loop == typeof(object))
326 break;
327 }
328
329 List<Microsoft.CodeAnalysis.MetadataReference> References = new List<Microsoft.CodeAnalysis.MetadataReference>();
330
331 foreach (string Location in Dependencies.Keys)
332 {
333 if (!string.IsNullOrEmpty(Location))
334 References.Add(Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(Location));
335 }
336
337 sb.Append("WSDA.");
338 AppendType(Type, sb);
339
340 Microsoft.CodeAnalysis.CSharp.CSharpCompilation Compilation =
341 Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Create(sb.ToString(),
342 new Microsoft.CodeAnalysis.SyntaxTree[] { Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(CSharpCode) },
343 References, new Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions(
344 Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary));
345
346 MemoryStream Output = new MemoryStream();
347 MemoryStream PdbOutput = new MemoryStream();
348
349 EmitResult CompilerResults = Compilation.Emit(Output, pdbStream: PdbOutput);
350
351 if (!CompilerResults.Success)
352 {
353 sb.Clear();
354
355 sb.Append("Unable to create a script proxy for callback methods of type ");
356 AppendType(Type, sb);
357 sb.AppendLine(". When generating proxy class, the following compiler errors were reported:");
358
359 foreach (Microsoft.CodeAnalysis.Diagnostic Error in CompilerResults.Diagnostics)
360 {
361 sb.AppendLine();
362 sb.Append(Error.Location.ToString());
363 sb.Append(": ");
364 sb.Append(Error.GetMessage());
365 }
366
367 sb.AppendLine();
368 sb.AppendLine();
369 sb.AppendLine("Code generated:");
370 sb.AppendLine();
371 sb.AppendLine(CSharpCode);
372
373 throw new ScriptRuntimeException(sb.ToString(), this);
374 }
375
376 Output.Position = 0;
377 PdbOutput.Position = 0;
378 Assembly A;
379
380 A = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(Output, PdbOutput);
381
382 sb.Clear();
383 sb.Append(Type.Namespace);
384 sb.Append(".ScriptCallbacks.ScriptProxy");
385 sb.Append(ClassTypeName);
386
387 ScriptProxyType = A.GetType(sb.ToString());
388 scriptProxyTypes[Type] = ScriptProxyType;
389 }
390 }
391
392 IScriptProxy Proxy = (IScriptProxy)Activator.CreateInstance(ScriptProxyType, Lambda, Variables);
393
394 return new ObjectValue(Proxy.GetCallbackFunctionUntyped());
395 }
396
397 private static readonly TypeInfo delegateTypeInfo = typeof(Delegate).GetTypeInfo();
398 private static readonly TypeInfo taskTypeInfo = typeof(Task).GetTypeInfo();
399 private static readonly Dictionary<Type, Type> scriptProxyTypes = new Dictionary<Type, Type>();
400
401 private static string GetLocation(Type T)
402 {
403 TypeInfo TI = T.GetTypeInfo();
404 string s = TI.Assembly.Location;
405
406 if (!string.IsNullOrEmpty(s))
407 return s;
408
409 return Path.Combine(Path.GetDirectoryName(GetLocation(typeof(Expression))), TI.Module.ScopeName);
410 }
411
412 private static void AppendType(Type T, StringBuilder sb)
413 {
414 if (T.IsConstructedGenericType)
415 {
416 Type T2 = T.GetGenericTypeDefinition();
417 string s = T2.FullName;
418 int i = s.IndexOf('`');
419
420 if (i > 0)
421 s = s.Substring(0, i);
422
423 sb.Append(s);
424 sb.Append('<');
425
426 bool First = true;
427
428 foreach (Type Arg in T.GenericTypeArguments)
429 {
430 if (First)
431 First = false;
432 else
433 sb.Append(',');
434
435 AppendType(Arg, sb);
436 }
437
438 sb.Append('>');
439 }
440 else if (T.HasElementType)
441 {
442 if (T.IsArray)
443 {
444 AppendType(T.GetElementType(), sb);
445 sb.Append("[]");
446 }
447 else if (T.IsPointer)
448 {
449 AppendType(T.GetElementType(), sb);
450 sb.Append('*');
451 }
452 else
453 sb.Append(T.FullName);
454 }
455 else
456 sb.Append(T.FullName);
457 }
458
459 }
460}
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
Generates a callback function based on script.
Definition: Callback.cs:20
override IElement Evaluate(IElement[] Arguments, Variables Variables)
Evaluates the function.
Definition: Callback.cs:64
Callback(ScriptNode DelegateType, ScriptNode Lambda, int Start, int Length, Expression Expression)
Generates a callback function based on script.
Definition: Callback.cs:29
override string FunctionName
Name of the function
Definition: Callback.cs:51
override string[] DefaultArgumentNames
Default Argument names
Definition: Callback.cs:56
Callback(ScriptNode DelegateType, ScriptNode ArgumentType, ScriptNode Lambda, int Start, int Length, Expression Expression)
Generates a callback function based on script.
Definition: Callback.cs:43
Class managing a script expression.
Definition: Expression.cs:39
Base class for multivariate funcions.
ScriptNode[] Arguments
Function arguments.
static readonly ArgumentType[] argumentTypes2Scalar
Two scalar parameters.
static readonly ArgumentType[] argumentTypes3Scalar
Three scalar parameters.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
Expression Expression
Expression of which the node is a part.
Definition: ScriptNode.cs:177
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
Collection of variables.
Definition: Variables.cs:25
Basic interface for all types of elements.
Definition: IElement.cs:20
Abstract base class for script proxies used by callback functions.
Definition: IScriptProxy.cs:9
object GetCallbackFunctionUntyped()
Untyped callback function.
Base interface for lambda expressions.
ArgumentType
Type of parameter used in a function definition or a lambda definition.
Definition: IFunction.cs:9