1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Dynamic.Utils;
8 using System.Reflection;
9 using System.Runtime.CompilerServices;
10 
11 namespace System.Linq.Expressions.Interpreter
12 {
13     internal sealed class CreateDelegateInstruction : Instruction
14     {
15         private readonly LightDelegateCreator _creator;
16 
CreateDelegateInstruction(LightDelegateCreator delegateCreator)17         internal CreateDelegateInstruction(LightDelegateCreator delegateCreator)
18         {
19             _creator = delegateCreator;
20         }
21 
22         public override int ConsumedStack => _creator.Interpreter.ClosureSize;
23         public override int ProducedStack => 1;
24         public override string InstructionName => "CreateDelegate";
25 
Run(InterpretedFrame frame)26         public override int Run(InterpretedFrame frame)
27         {
28             IStrongBox[] closure;
29             if (ConsumedStack > 0)
30             {
31                 closure = new IStrongBox[ConsumedStack];
32                 for (int i = closure.Length - 1; i >= 0; i--)
33                 {
34                     closure[i] = (IStrongBox)frame.Pop();
35                 }
36             }
37             else
38             {
39                 closure = null;
40             }
41 
42             Delegate d = _creator.CreateDelegate(closure);
43 
44             frame.Push(d);
45             return 1;
46         }
47     }
48 
49     internal sealed class TypeIsInstruction : Instruction
50     {
51         private readonly Type _type;
52 
TypeIsInstruction(Type type)53         internal TypeIsInstruction(Type type)
54         {
55             _type = type;
56         }
57 
58         public override int ConsumedStack => 1;
59         public override int ProducedStack => 1;
60         public override string InstructionName => "TypeIs";
61 
Run(InterpretedFrame frame)62         public override int Run(InterpretedFrame frame)
63         {
64             frame.Push(_type.IsInstanceOfType(frame.Pop()));
65             return 1;
66         }
67 
ToString()68         public override string ToString() => "TypeIs " + _type.ToString();
69     }
70 
71     internal sealed class TypeAsInstruction : Instruction
72     {
73         private readonly Type _type;
74 
TypeAsInstruction(Type type)75         internal TypeAsInstruction(Type type)
76         {
77             _type = type;
78         }
79 
80         public override int ConsumedStack => 1;
81         public override int ProducedStack => 1;
82         public override string InstructionName => "TypeAs";
83 
Run(InterpretedFrame frame)84         public override int Run(InterpretedFrame frame)
85         {
86             object value = frame.Pop();
87             frame.Push(_type.IsInstanceOfType(value) ? value : null);
88             return 1;
89         }
90 
ToString()91         public override string ToString() => "TypeAs " + _type.ToString();
92     }
93 
94     internal sealed class TypeEqualsInstruction : Instruction
95     {
96         public static readonly TypeEqualsInstruction Instance = new TypeEqualsInstruction();
97 
98         public override int ConsumedStack => 2;
99         public override int ProducedStack => 1;
100         public override string InstructionName => "TypeEquals";
101 
TypeEqualsInstruction()102         private TypeEqualsInstruction() { }
103 
Run(InterpretedFrame frame)104         public override int Run(InterpretedFrame frame)
105         {
106             object type = frame.Pop();
107             object obj = frame.Pop();
108             frame.Push((object)obj?.GetType() == type);
109             return 1;
110         }
111     }
112 
113     internal abstract class NullableMethodCallInstruction : Instruction
114     {
115         private static NullableMethodCallInstruction s_hasValue, s_value, s_equals, s_getHashCode, s_getValueOrDefault1, s_toString;
116 
117         public override int ConsumedStack => 1;
118         public override int ProducedStack => 1;
119         public override string InstructionName => "NullableMethod";
120 
NullableMethodCallInstruction()121         private NullableMethodCallInstruction() { }
122 
123         private sealed class HasValue : NullableMethodCallInstruction
124         {
Run(InterpretedFrame frame)125             public override int Run(InterpretedFrame frame)
126             {
127                 object obj = frame.Pop();
128                 frame.Push(obj != null);
129                 return 1;
130             }
131         }
132 
133         private sealed class GetValue : NullableMethodCallInstruction
134         {
Run(InterpretedFrame frame)135             public override int Run(InterpretedFrame frame)
136             {
137                 if (frame.Peek() == null)
138                 {
139                     // Trigger InvalidOperationException with same localized method as if we'd called the Value getter.
140                     return (int)default(int?);
141                 }
142 
143                 return 1;
144             }
145         }
146 
147         private sealed class GetValueOrDefault : NullableMethodCallInstruction
148         {
149             private readonly Type _defaultValueType;
150 
GetValueOrDefault(MethodInfo mi)151             public GetValueOrDefault(MethodInfo mi)
152             {
153                 _defaultValueType = mi.ReturnType;
154             }
155 
Run(InterpretedFrame frame)156             public override int Run(InterpretedFrame frame)
157             {
158                 if (frame.Peek() == null)
159                 {
160                     frame.Pop();
161                     frame.Push(Activator.CreateInstance(_defaultValueType));
162                 }
163                 return 1;
164             }
165         }
166 
167         private sealed class GetValueOrDefault1 : NullableMethodCallInstruction
168         {
169             public override int ConsumedStack => 2;
170 
Run(InterpretedFrame frame)171             public override int Run(InterpretedFrame frame)
172             {
173                 object dflt = frame.Pop();
174                 object obj = frame.Pop();
175                 frame.Push(obj ?? dflt);
176                 return 1;
177             }
178         }
179 
180         private sealed class EqualsClass : NullableMethodCallInstruction
181         {
182             public override int ConsumedStack => 2;
183 
Run(InterpretedFrame frame)184             public override int Run(InterpretedFrame frame)
185             {
186                 object other = frame.Pop();
187                 object obj = frame.Pop();
188                 if (obj == null)
189                 {
190                     frame.Push(other == null);
191                 }
192                 else if (other == null)
193                 {
194                     frame.Push(Utils.BoxedFalse);
195                 }
196                 else
197                 {
198                     frame.Push(obj.Equals(other));
199                 }
200                 return 1;
201             }
202         }
203 
204         private sealed class ToStringClass : NullableMethodCallInstruction
205         {
Run(InterpretedFrame frame)206             public override int Run(InterpretedFrame frame)
207             {
208                 object obj = frame.Pop();
209                 frame.Push(obj == null ? "" : obj.ToString());
210                 return 1;
211             }
212         }
213 
214         private sealed class GetHashCodeClass : NullableMethodCallInstruction
215         {
Run(InterpretedFrame frame)216             public override int Run(InterpretedFrame frame)
217             {
218                 object obj = frame.Pop();
219                 frame.Push(obj?.GetHashCode() ?? 0);
220                 return 1;
221             }
222         }
223 
Create(string method, int argCount, MethodInfo mi)224         public static Instruction Create(string method, int argCount, MethodInfo mi)
225         {
226             switch (method)
227             {
228                 case "get_HasValue": return s_hasValue ?? (s_hasValue = new HasValue());
229                 case "get_Value": return s_value ?? (s_value = new GetValue());
230                 case "Equals": return s_equals ?? (s_equals = new EqualsClass());
231                 case "GetHashCode": return s_getHashCode ?? (s_getHashCode = new GetHashCodeClass());
232                 case "GetValueOrDefault":
233                     if (argCount == 0)
234                     {
235                         return new GetValueOrDefault(mi);
236                     }
237                     else
238                     {
239                         return s_getValueOrDefault1 ?? (s_getValueOrDefault1 = new GetValueOrDefault1());
240                     }
241                 case "ToString": return s_toString ?? (s_toString = new ToStringClass());
242                 default:
243                     // System.Nullable doesn't have other instance methods
244                     throw ContractUtils.Unreachable;
245             }
246         }
247 
CreateGetValue()248         public static Instruction CreateGetValue()
249         {
250             return s_value ?? (s_value = new GetValue());
251         }
252     }
253 
254     internal abstract class CastInstruction : Instruction
255     {
256         private static CastInstruction s_Boolean, s_Byte, s_Char, s_DateTime, s_Decimal, s_Double, s_Int16, s_Int32, s_Int64, s_SByte, s_Single, s_String, s_UInt16, s_UInt32, s_UInt64;
257 
258         public override int ConsumedStack => 1;
259         public override int ProducedStack => 1;
260         public override string InstructionName => "Cast";
261 
262         private sealed class CastInstructionT<T> : CastInstruction
263         {
Run(InterpretedFrame frame)264             public override int Run(InterpretedFrame frame)
265             {
266                 object value = frame.Pop();
267                 frame.Push((T)value);
268                 return 1;
269             }
270         }
271 
272         private abstract class CastInstructionNoT : CastInstruction
273         {
274             private readonly Type _t;
275 
CastInstructionNoT(Type t)276             protected CastInstructionNoT(Type t)
277             {
278                 _t = t;
279             }
280 
Create(Type t)281             public new static CastInstruction Create(Type t)
282             {
283                 if (t.IsValueType && !t.IsNullableType())
284                 {
285                     return new Value(t);
286                 }
287                 else
288                 {
289                     return new Ref(t);
290                 }
291             }
292 
Run(InterpretedFrame frame)293             public override int Run(InterpretedFrame frame)
294             {
295                 object value = frame.Pop();
296                 if (value != null)
297                 {
298                     Type valueType = value.GetType();
299 
300                     if (!valueType.HasReferenceConversionTo(_t) &&
301                         !valueType.HasIdentityPrimitiveOrNullableConversionTo(_t))
302                     {
303                         throw new InvalidCastException();
304                     }
305 
306                     if (!_t.IsAssignableFrom(valueType))
307                     {
308                         throw new InvalidCastException();
309                     }
310 
311                     frame.Push(value);
312                 }
313                 else
314                 {
315                     ConvertNull(frame);
316                 }
317                 return 1;
318             }
319 
ConvertNull(InterpretedFrame frame)320             protected abstract void ConvertNull(InterpretedFrame frame);
321 
322             private sealed class Ref : CastInstructionNoT
323             {
Ref(Type t)324                 public Ref(Type t)
325                     : base(t)
326                 {
327                 }
328 
ConvertNull(InterpretedFrame frame)329                 protected override void ConvertNull(InterpretedFrame frame)
330                 {
331                     frame.Push(null);
332                 }
333             }
334 
335             private sealed class Value : CastInstructionNoT
336             {
Value(Type t)337                 public Value(Type t)
338                     : base(t)
339                 {
340                 }
341 
ConvertNull(InterpretedFrame frame)342                 protected override void ConvertNull(InterpretedFrame frame)
343                 {
344                     throw new NullReferenceException();
345                 }
346             }
347         }
348 
Create(Type t)349         public static Instruction Create(Type t)
350         {
351             Debug.Assert(!t.IsEnum);
352             switch (t.GetTypeCode())
353             {
354                 case TypeCode.Boolean: return s_Boolean ?? (s_Boolean = new CastInstructionT<bool>());
355                 case TypeCode.Byte: return s_Byte ?? (s_Byte = new CastInstructionT<byte>());
356                 case TypeCode.Char: return s_Char ?? (s_Char = new CastInstructionT<char>());
357                 case TypeCode.DateTime: return s_DateTime ?? (s_DateTime = new CastInstructionT<DateTime>());
358                 case TypeCode.Decimal: return s_Decimal ?? (s_Decimal = new CastInstructionT<decimal>());
359                 case TypeCode.Double: return s_Double ?? (s_Double = new CastInstructionT<double>());
360                 case TypeCode.Int16: return s_Int16 ?? (s_Int16 = new CastInstructionT<short>());
361                 case TypeCode.Int32: return s_Int32 ?? (s_Int32 = new CastInstructionT<int>());
362                 case TypeCode.Int64: return s_Int64 ?? (s_Int64 = new CastInstructionT<long>());
363                 case TypeCode.SByte: return s_SByte ?? (s_SByte = new CastInstructionT<sbyte>());
364                 case TypeCode.Single: return s_Single ?? (s_Single = new CastInstructionT<float>());
365                 case TypeCode.String: return s_String ?? (s_String = new CastInstructionT<string>());
366                 case TypeCode.UInt16: return s_UInt16 ?? (s_UInt16 = new CastInstructionT<ushort>());
367                 case TypeCode.UInt32: return s_UInt32 ?? (s_UInt32 = new CastInstructionT<uint>());
368                 case TypeCode.UInt64: return s_UInt64 ?? (s_UInt64 = new CastInstructionT<ulong>());
369             }
370 
371             return CastInstructionNoT.Create(t);
372         }
373     }
374 
375     internal sealed class CastToEnumInstruction : CastInstruction
376     {
377         private readonly Type _t;
378 
CastToEnumInstruction(Type t)379         public CastToEnumInstruction(Type t)
380         {
381             Debug.Assert(t.IsEnum);
382             _t = t;
383         }
384 
Run(InterpretedFrame frame)385         public override int Run(InterpretedFrame frame)
386         {
387             object from = frame.Pop();
388             Debug.Assert(
389                 new[]
390                 {
391                     TypeCode.Empty, TypeCode.Int32, TypeCode.SByte, TypeCode.Int16, TypeCode.Int64, TypeCode.UInt32,
392                     TypeCode.Byte, TypeCode.UInt16, TypeCode.UInt64, TypeCode.Char, TypeCode.Boolean
393                 }.Contains(Convert.GetTypeCode(from)));
394             frame.Push(from == null ? null : Enum.ToObject(_t, from));
395             return 1;
396         }
397     }
398 
399     internal sealed class CastReferenceToEnumInstruction : CastInstruction
400     {
401         private readonly Type _t;
402 
CastReferenceToEnumInstruction(Type t)403         public CastReferenceToEnumInstruction(Type t)
404         {
405             Debug.Assert(t.IsEnum);
406             _t = t;
407         }
408 
Run(InterpretedFrame frame)409         public override int Run(InterpretedFrame frame)
410         {
411             object from = frame.Pop();
412             Debug.Assert(from != null);
413 
414             // If from is neither a T nor a type assignable to T (viz. an T-backed enum)
415             // this will cause an InvalidCastException, which is what this operation should
416             // throw in this case.
417 
418             switch (_t.GetTypeCode())
419             {
420                 case TypeCode.Int32:
421                     frame.Push(Enum.ToObject(_t, (int)from));
422                     break;
423                 case TypeCode.Int64:
424                     frame.Push(Enum.ToObject(_t, (long)from));
425                     break;
426                 case TypeCode.UInt32:
427                     frame.Push(Enum.ToObject(_t, (uint)from));
428                     break;
429                 case TypeCode.UInt64:
430                     frame.Push(Enum.ToObject(_t, (ulong)from));
431                     break;
432                 case TypeCode.Byte:
433                     frame.Push(Enum.ToObject(_t, (byte)from));
434                     break;
435                 case TypeCode.SByte:
436                     frame.Push(Enum.ToObject(_t, (sbyte)from));
437                     break;
438                 case TypeCode.Int16:
439                     frame.Push(Enum.ToObject(_t, (short)from));
440                     break;
441                 case TypeCode.UInt16:
442                     frame.Push(Enum.ToObject(_t, (ushort)from));
443                     break;
444                 case TypeCode.Char:
445                     // Disallowed in C#, but allowed in CIL
446                     frame.Push(Enum.ToObject(_t, (char)from));
447                     break;
448                 default:
449                     // Only remaining possible type.
450                     // Disallowed in C#, but allowed in CIL
451                     Debug.Assert(_t.GetTypeCode() == TypeCode.Boolean);
452                     frame.Push(Enum.ToObject(_t, (bool)from));
453                     break;
454             }
455 
456             return 1;
457         }
458     }
459 
460     internal sealed class QuoteInstruction : Instruction
461     {
462         private readonly Expression _operand;
463         private readonly Dictionary<ParameterExpression, LocalVariable> _hoistedVariables;
464 
QuoteInstruction(Expression operand, Dictionary<ParameterExpression, LocalVariable> hoistedVariables)465         public QuoteInstruction(Expression operand, Dictionary<ParameterExpression, LocalVariable> hoistedVariables)
466         {
467             _operand = operand;
468             _hoistedVariables = hoistedVariables;
469         }
470 
471         public override int ProducedStack => 1;
472 
473         public override string InstructionName => "Quote";
474 
Run(InterpretedFrame frame)475         public override int Run(InterpretedFrame frame)
476         {
477             Expression operand = _operand;
478             if (_hoistedVariables != null)
479             {
480                 operand = new ExpressionQuoter(_hoistedVariables, frame).Visit(operand);
481             }
482             frame.Push(operand);
483             return 1;
484         }
485 
486         // Modifies a quoted Expression instance by changing hoisted variables and
487         // parameters into hoisted local references. The variable's StrongBox is
488         // burned as a constant, and all hoisted variables/parameters are rewritten
489         // as indexing expressions.
490         //
491         // The behavior of Quote is intended to be like C# and VB expression quoting
492         private sealed class ExpressionQuoter : ExpressionVisitor
493         {
494             private readonly Dictionary<ParameterExpression, LocalVariable> _variables;
495             private readonly InterpretedFrame _frame;
496 
497             // A stack of variables that are defined in nested scopes. We search
498             // this first when resolving a variable in case a nested scope shadows
499             // one of our variable instances.
500             private readonly Stack<HashSet<ParameterExpression>> _shadowedVars = new Stack<HashSet<ParameterExpression>>();
501 
ExpressionQuoter(Dictionary<ParameterExpression, LocalVariable> hoistedVariables, InterpretedFrame frame)502             internal ExpressionQuoter(Dictionary<ParameterExpression, LocalVariable> hoistedVariables, InterpretedFrame frame)
503             {
504                 _variables = hoistedVariables;
505                 _frame = frame;
506             }
507 
VisitLambda(Expression<T> node)508             protected internal override Expression VisitLambda<T>(Expression<T> node)
509             {
510                 if (node.ParameterCount > 0)
511                 {
512                     var parameters = new HashSet<ParameterExpression>();
513 
514                     for (int i = 0, n = node.ParameterCount; i < n; i++)
515                     {
516                         parameters.Add(node.GetParameter(i));
517                     }
518 
519                     _shadowedVars.Push(parameters);
520                 }
521                 Expression b = Visit(node.Body);
522                 if (node.ParameterCount > 0)
523                 {
524                     _shadowedVars.Pop();
525                 }
526                 if (b == node.Body)
527                 {
528                     return node;
529                 }
530                 return node.Rewrite(b, parameters: null);
531             }
532 
VisitBlock(BlockExpression node)533             protected internal override Expression VisitBlock(BlockExpression node)
534             {
535                 if (node.Variables.Count > 0)
536                 {
537                     _shadowedVars.Push(new HashSet<ParameterExpression>(node.Variables));
538                 }
539                 Expression[] b = ExpressionVisitorUtils.VisitBlockExpressions(this, node);
540                 if (node.Variables.Count > 0)
541                 {
542                     _shadowedVars.Pop();
543                 }
544                 if (b == null)
545                 {
546                     return node;
547                 }
548                 return node.Rewrite(node.Variables, b);
549             }
550 
VisitCatchBlock(CatchBlock node)551             protected override CatchBlock VisitCatchBlock(CatchBlock node)
552             {
553                 if (node.Variable != null)
554                 {
555                     _shadowedVars.Push(new HashSet<ParameterExpression> { node.Variable });
556                 }
557                 Expression b = Visit(node.Body);
558                 Expression f = Visit(node.Filter);
559                 if (node.Variable != null)
560                 {
561                     _shadowedVars.Pop();
562                 }
563                 if (b == node.Body && f == node.Filter)
564                 {
565                     return node;
566                 }
567                 return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
568             }
569 
VisitRuntimeVariables(RuntimeVariablesExpression node)570             protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
571             {
572                 int count = node.Variables.Count;
573                 var boxes = new List<IStrongBox>();
574                 var vars = new List<ParameterExpression>();
575                 var indexes = new int[count];
576                 for (int i = 0; i < indexes.Length; i++)
577                 {
578                     IStrongBox box = GetBox(node.Variables[i]);
579                     if (box == null)
580                     {
581                         indexes[i] = vars.Count;
582                         vars.Add(node.Variables[i]);
583                     }
584                     else
585                     {
586                         indexes[i] = -1 - boxes.Count;
587                         boxes.Add(box);
588                     }
589                 }
590 
591                 // No variables were rewritten. Just return the original node.
592                 if (boxes.Count == 0)
593                 {
594                     return node;
595                 }
596 
597                 ConstantExpression boxesConst = Expression.Constant(new RuntimeOps.RuntimeVariables(boxes.ToArray()), typeof(IRuntimeVariables));
598                 // All of them were rewritten. Just return the array as a constant
599                 if (vars.Count == 0)
600                 {
601                     return boxesConst;
602                 }
603 
604                 // Otherwise, we need to return an object that merges them.
605                 return Expression.Invoke(
606                     Expression.Constant(new Func<IRuntimeVariables, IRuntimeVariables, int[], IRuntimeVariables>(MergeRuntimeVariables)),
607                     Expression.RuntimeVariables(new TrueReadOnlyCollection<ParameterExpression>(vars.ToArray())),
608                     boxesConst,
609                     Expression.Constant(indexes)
610                 );
611             }
612 
MergeRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes)613             private static IRuntimeVariables MergeRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes)
614             {
615                 return new RuntimeOps.MergedRuntimeVariables(first, second, indexes);
616             }
617 
VisitParameter(ParameterExpression node)618             protected internal override Expression VisitParameter(ParameterExpression node)
619             {
620                 IStrongBox box = GetBox(node);
621                 if (box == null)
622                 {
623                     return node;
624                 }
625                 return Expression.Convert(Expression.Field(Expression.Constant(box), "Value"), node.Type);
626             }
627 
GetBox(ParameterExpression variable)628             private IStrongBox GetBox(ParameterExpression variable)
629             {
630                 LocalVariable var;
631                 if (_variables.TryGetValue(variable, out var))
632                 {
633                     if (var.InClosure)
634                     {
635                         return _frame.Closure[var.Index];
636                     }
637                     else
638                     {
639                         return (IStrongBox)_frame.Data[var.Index];
640                     }
641                 }
642 
643                 return null;
644             }
645         }
646     }
647 }
648