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