private void Emit(BlockExpression node, CompilationFlags flags) { int count = node.ExpressionCount; if (count == 0) { return; } EnterScope(node); CompilationFlags emitAs = flags & CompilationFlags.EmitAsTypeMask; CompilationFlags tailCall = flags & CompilationFlags.EmitAsTailCallMask; for (int index = 0; index < count - 1; index++) { var e = node.GetExpression(index); var next = node.GetExpression(index + 1); CompilationFlags tailCallFlag; if (tailCall != CompilationFlags.EmitAsNoTail) { var g = next as GotoExpression; if (g != null && (g.Value == null || !Significant(g.Value)) && ReferenceLabel(g.Target).CanReturn) { // Since tail call flags are not passed into EmitTryExpression, CanReturn means the goto will be emitted // as Ret. Therefore we can emit the current expression with tail call. tailCallFlag = CompilationFlags.EmitAsTail; } else { // In the middle of the block. // We may do better here by marking it as Tail if the following expressions are not going to emit any IL. tailCallFlag = CompilationFlags.EmitAsMiddle; } } else { tailCallFlag = CompilationFlags.EmitAsNoTail; } flags = UpdateEmitAsTailCallFlag(flags, tailCallFlag); EmitExpressionAsVoid(e, flags); } // if the type of Block it means this is not a Comma // so we will force the last expression to emit as void. // We don't need EmitAsType flag anymore, should only pass // the EmitTailCall field in flags to emitting the last expression. if (emitAs == CompilationFlags.EmitAsVoidType || node.Type == typeof(void)) { EmitExpressionAsVoid(node.GetExpression(count - 1), tailCall); } else { EmitExpressionAsType(node.GetExpression(count - 1), node.Type, tailCall); } ExitScope(node); }
private void EmitConditionalExpression(Expression expr, CompilationFlags flags) { ConditionalExpression node = (ConditionalExpression)expr; Debug.Assert(node.Test.Type == typeof(bool)); Label labFalse = _ilg.DefineLabel(); EmitExpressionAndBranch(false, node.Test, labFalse); EmitExpressionAsType(node.IfTrue, node.Type, flags); if (NotEmpty(node.IfFalse)) { Label labEnd = _ilg.DefineLabel(); if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail) { // We know the conditional expression is at the end of the lambda, // so it is safe to emit Ret here. _ilg.Emit(OpCodes.Ret); } else { _ilg.Emit(OpCodes.Br, labEnd); } _ilg.MarkLabel(labFalse); EmitExpressionAsType(node.IfFalse, node.Type, flags); _ilg.MarkLabel(labEnd); } else { _ilg.MarkLabel(labFalse); } }
private void EmitExpressionAsVoid(Expression node, CompilationFlags flags) { Debug.Assert(node != null); CompilationFlags startEmitted = EmitExpressionStart(node); switch (node.NodeType) { case ExpressionType.Assign: EmitAssign((BinaryExpression)node, CompilationFlags.EmitAsVoidType); break; case ExpressionType.Block: Emit((BlockExpression)node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType)); break; case ExpressionType.Throw: EmitThrow((UnaryExpression)node, CompilationFlags.EmitAsVoidType); break; case ExpressionType.Goto: EmitGotoExpression(node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType)); break; case ExpressionType.Constant: case ExpressionType.Default: case ExpressionType.Parameter: // no-op break; default: if (node.Type == typeof(void)) { EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart)); } else { EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart); _ilg.Emit(OpCodes.Pop); } break; } EmitExpressionEnd(startEmitted); }
private static bool TryParseArgs(string[] args, out string sourceFile, out CompilationFlags flags) { sourceFile = null; flags = 0; if (args.Length < 1) return false; foreach (string arg in args) { string normalizedArg = arg.ToUpper(); switch (normalizedArg) { case "/32": flags |= CompilationFlags.Platform32; break; case "/64": flags |= CompilationFlags.Platform64; break; case "/NODEBUG": flags |= CompilationFlags.NoDebug; break; case "/ASM": flags |= CompilationFlags.Assembly; break; default: if (normalizedArg.StartsWith("/")) { Console.WriteLine("Unrecognized option {0}", normalizedArg); return false; } if (!File.Exists(arg)) { Console.WriteLine("Source file '{0}' not found", arg); return false; } if (sourceFile != null) { Console.WriteLine("Multiple source files specified. Only one file is supported"); return false; } sourceFile = arg; break; } } if (flags.HasFlag(CompilationFlags.Platform32) && flags.HasFlag(CompilationFlags.Platform64)) { Console.WriteLine("Both 32-bit and 64-bit platforms specified. Only one platform is supported"); return false; } if (!flags.HasFlag(CompilationFlags.Platform32) && !flags.HasFlag(CompilationFlags.Platform64)) flags |= CompilationFlags.Platform32; if (sourceFile == null) return false; return true; }
/// <summary> /// Initializes a new instance of the PeEmitter class. /// Use this constructor to when saving the PE file to disk /// </summary> /// <param name="outputFile">Path to output file</param> /// <param name="flags">Compiler flags</param> public PeEmitter(string outputFile, CompilationFlags flags) { _ilFile = GetPathToTempFile("il"); _textEmitter = new TextEmitter(_ilFile); _outputFile = outputFile; _flags = flags; }
protected CompilerContext(string filePath, StreamReader inputReader, Importer importer, IEmitter emitter, CompilationFlags flags) { FilePath = filePath; Flags = flags; Emitter = emitter; CompileErrors = new ErrorList(); Importer = importer; SymbolTable = new SymbolTable(); Lexer = Lexer.Create(inputReader, CompileErrors); }
private TestCompilerContext( MemoryStream input, StreamReader reader, MemoryStream output, IEmitter emitter, CompilationFlags flags) : base("FakeFile.iris", reader, emitter, flags) { _input = input; _reader = reader; _output = output; }
protected CmdLineCompilerContext( string sourcePath, Stream inputFile, StreamReader sourceReader, IEmitter emitter, CompilationFlags flags) : base(sourcePath, sourceReader, emitter, flags) { _inputFile = inputFile; _reader = sourceReader; _emitter = emitter; }
private void EmitThrow(UnaryExpression expr, CompilationFlags flags) { if (expr.Operand == null) { CheckRethrow(); _ilg.Emit(OpCodes.Rethrow); } else { EmitExpression(expr.Operand); _ilg.Emit(OpCodes.Throw); } EmitUnreachable(expr, flags); }
private void Emit(BlockExpression node, CompilationFlags flags) { EnterScope(node); CompilationFlags emitAs = flags & CompilationFlags.EmitAsTypeMask; int count = node.ExpressionCount; CompilationFlags tailCall = flags & CompilationFlags.EmitAsTailCallMask; CompilationFlags middleTailCall = tailCall == CompilationFlags.EmitAsNoTail ? CompilationFlags.EmitAsNoTail : CompilationFlags.EmitAsMiddle; for (int index = 0; index < count - 1; index++) { var e = node.GetExpression(index); var next = node.GetExpression(index + 1); if (EmitDebugSymbols) { // No need to emit a clearance if the next expression in the block is also a // DebugInfoExprssion. var debugInfo = e as DebugInfoExpression; if (debugInfo != null && debugInfo.IsClear && next is DebugInfoExpression) { continue; } } // In the middle of the block. // We may do better here by marking it as Tail if the following expressions are not going to emit any IL. var tailCallFlag = middleTailCall; var g = next as GotoExpression; if (g != null && (g.Value == null || !Significant(g.Value))) { var labelInfo = ReferenceLabel(g.Target); if (labelInfo.CanReturn) { // Since tail call flags are not passed into EmitTryExpression, CanReturn means the goto will be emitted // as Ret. Therefore we can emit the current expression with tail call. tailCallFlag = CompilationFlags.EmitAsTail; } } flags = UpdateEmitAsTailCallFlag(flags, tailCallFlag); EmitExpressionAsVoid(e, flags); } // if the type of Block it means this is not a Comma // so we will force the last expression to emit as void. // We don't need EmitAsType flag anymore, should only pass // the EmitTailCall field in flags to emitting the last expression. if (emitAs == CompilationFlags.EmitAsVoidType || node.Type == typeof(void)) { EmitExpressionAsVoid(node.GetExpression(count - 1), tailCall); } else { EmitExpressionAsType(node.GetExpression(count - 1), node.Type, tailCall); } ExitScope(node); }
private void EmitBinaryExpression(Expression expr, CompilationFlags flags) { BinaryExpression b = (BinaryExpression)expr; Debug.Assert(b.NodeType != ExpressionType.AndAlso && b.NodeType != ExpressionType.OrElse && b.NodeType != ExpressionType.Coalesce); if (b.Method != null) { EmitBinaryMethod(b, flags); return; } // For EQ and NE, if there is a user-specified method, use it. // Otherwise implement the C# semantics that allow equality // comparisons on non-primitive nullable structs that don't // overload "==" if ((b.NodeType == ExpressionType.Equal || b.NodeType == ExpressionType.NotEqual) && (b.Type == typeof(bool) || b.Type == typeof(bool?))) { // If we have x==null, x!=null, null==x or null!=x where x is // nullable but not null, then generate a call to x.HasValue. Debug.Assert(!b.IsLiftedToNull || b.Type == typeof(bool?)); if (ConstantCheck.IsNull(b.Left) && !ConstantCheck.IsNull(b.Right) && TypeHelper.IsNullableType(b.Right.Type)) { EmitNullEquality(b.NodeType, b.Right, b.IsLiftedToNull); return; } if (ConstantCheck.IsNull(b.Right) && !ConstantCheck.IsNull(b.Left) && TypeHelper.IsNullableType(b.Left.Type)) { EmitNullEquality(b.NodeType, b.Left, b.IsLiftedToNull); return; } // For EQ and NE, we can avoid some conversions if we're // ultimately just comparing two managed pointers. EmitExpression(GetEqualityOperand(b.Left)); EmitExpression(GetEqualityOperand(b.Right)); } else { // Otherwise generate it normally EmitExpression(b.Left); EmitExpression(b.Right); } EmitBinaryOperator(b.NodeType, b.Left.Type, b.Right.Type, b.Type, b.IsLiftedToNull); }
public static TestCompilerContext Create(string compiland, GlobalSymbolList globals, CompilationFlags flags) { byte[] buffer = Encoding.Default.GetBytes(compiland); MemoryStream input = new MemoryStream(buffer); StreamReader reader = new StreamReader(input); MemoryStream output = new MemoryStream(); TextEmitter emitter = new TextEmitter(output); TestCompilerContext testContext = new TestCompilerContext(input, reader, output, emitter, flags); if (globals != null) { foreach (var symbol in globals.Items) testContext.SymbolTable.Add(symbol.Item1, symbol.Item2, StorageClass.Global, null); } return testContext; }
private void EmitUnary(UnaryExpression node, CompilationFlags flags) { if (node.Method != null) { EmitUnaryMethod(node, flags); } else if (node.NodeType == ExpressionType.NegateChecked && TypeUtils.IsInteger(node.Operand.Type)) { EmitExpression(node.Operand); LocalBuilder loc = GetLocal(node.Operand.Type); _ilg.Emit(OpCodes.Stloc, loc); _ilg.EmitInt(0); _ilg.EmitConvertToType(typeof(int), node.Operand.Type, false); _ilg.Emit(OpCodes.Ldloc, loc); FreeLocal(loc); EmitBinaryOperator(ExpressionType.SubtractChecked, node.Operand.Type, node.Operand.Type, node.Type, false); } else { EmitExpression(node.Operand); EmitUnaryOperator(node.NodeType, node.Operand.Type, node.Type); } }
private void EmitLabelExpression(Expression expr, CompilationFlags flags) { var node = (LabelExpression)expr; Debug.Assert(node.Target != null); // If we're an immediate child of a block, our label will already // be defined. If not, we need to define our own block so this // label isn't exposed except to its own child expression. LabelInfo label = null; if (_labelBlock.Kind == LabelScopeKind.Block) { _labelBlock.TryGetLabelInfo(node.Target, out label); // We're in a block but didn't find our label, try switch if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) { _labelBlock.Parent.TryGetLabelInfo(node.Target, out label); } // if we're in a switch or block, we should've found the label Debug.Assert(label != null); } if (label == null) { label = DefineLabel(node.Target); } if (node.DefaultValue != null) { if (node.Target.Type == typeof(void)) { EmitExpressionAsVoid(node.DefaultValue, flags); } else { flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart); EmitExpression(node.DefaultValue, flags); } } label.Mark(); }
// We don't want "ref" parameters to modify values of expressions // except where it would in IL: locals, args, fields, and array elements // (Unbox is an exception, it's intended to emit a ref to the original // boxed value) private void EmitAddress(Expression node, Type type, CompilationFlags flags) { Debug.Assert(node != null); bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart; CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart; switch (node.NodeType) { default: EmitExpressionAddress(node, type); break; case ExpressionType.ArrayIndex: AddressOf((BinaryExpression)node, type); break; case ExpressionType.Parameter: AddressOf((ParameterExpression)node, type); break; case ExpressionType.MemberAccess: AddressOf((MemberExpression)node, type); break; case ExpressionType.Unbox: AddressOf((UnaryExpression)node, type); break; case ExpressionType.Call: AddressOf((MethodCallExpression)node, type); break; case ExpressionType.Index: AddressOf((IndexExpression)node, type); break; } if (emitStart) { EmitExpressionEnd(startEmitted); } }
public static CmdLineCompilerContext Create(string sourcePath, CompilationFlags flags) { string outputFile; IEmitter emitter; if (flags.HasFlag(CompilationFlags.Assembly)) { outputFile = Path.ChangeExtension(sourcePath, "il"); emitter = new TextEmitter(outputFile); } else { outputFile = Path.ChangeExtension(sourcePath, "exe"); emitter = new PeEmitter(outputFile, flags); } Stream inputFile = File.Open(sourcePath, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader(inputFile); return new CmdLineCompilerContext(sourcePath, inputFile, reader, emitter, flags); }
private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) { if (type == typeof(void)) { EmitExpressionAsVoid(node, flags); } else { // if the node is emitted as a different type, CastClass IL is emitted at the end, // should not emit with tail calls. if (!TypeUtils.AreEquivalent(node.Type, type)) { EmitExpression(node); Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type)); _ilg.Emit(OpCodes.Castclass, type); } else { // emit the node with the flags and emit expression start EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart)); } } }
private void EmitBlockExpression(Expression expr, CompilationFlags flags) { // emit body Emit((BlockExpression)expr, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsDefaultType)); }
// assumes 'object' of non-static call is already on stack private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type?objectType, CompilationFlags flags) { // Emit arguments List <WriteBack>?wb = EmitArguments(mi, args); // Emit the actual call OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call; if (callOp == OpCodes.Callvirt && objectType !.IsValueType) { // This automatically boxes value types if necessary. _ilg.Emit(OpCodes.Constrained, objectType); } // The method call can be a tail call if // 1) the method call is the last instruction before Ret // 2) the method does not have any ByRef parameters, refer to ECMA-335 Partition III Section 2.4. // "Verification requires that no managed pointers are passed to the method being called, since // it does not track pointers into the current frame." if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && !MethodHasByRefParameter(mi)) { _ilg.Emit(OpCodes.Tailcall); } if (mi.CallingConvention == CallingConventions.VarArgs) { int count = args.ArgumentCount; Type[] types = new Type[count]; for (int i = 0; i < count; i++) { types[i] = args.GetArgument(i).Type; } _ilg.EmitCall(callOp, mi, types); } else { _ilg.Emit(callOp, mi); } // Emit write-backs for properties passed as "ref" arguments EmitWriteBack(wb); }
/// <summary> /// Update the flag with a new EmitAsType flag /// </summary> private static CompilationFlags UpdateEmitAsTypeFlag(CompilationFlags flags, CompilationFlags newValue) { Debug.Assert(newValue == CompilationFlags.EmitAsDefaultType || newValue == CompilationFlags.EmitAsVoidType); var oldValue = flags & CompilationFlags.EmitAsTypeMask; return flags ^ oldValue | newValue; }
private void EmitMethodCallExpression(Expression expr, CompilationFlags flags) { MethodCallExpression node = (MethodCallExpression)expr; EmitMethodCall(node.Object, node.Method, node, flags); }
private void EmitExpression(Expression node, CompilationFlags flags) { // When compiling deep trees, we run the risk of triggering a terminating StackOverflowException, // so we use the StackGuard utility here to probe for sufficient stack and continue the work on // another thread when we run out of stack space. if (!_guard.TryEnterOnCurrentStack()) { _guard.RunOnEmptyStack((@this, n, f) => @this.EmitExpression(n, f), this, node, flags); return; } var emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart; var labelScopeChangeInfo = GetLabelScopeChangeInfo(emitStart, _labelBlock, node); if (labelScopeChangeInfo.HasValue) { _labelBlock = new LabelScopeInfo(labelScopeChangeInfo.Value.parent, labelScopeChangeInfo.Value.kind); DefineBlockLabels(labelScopeChangeInfo.Value.nodes); } // only pass tail call flags to emit the expression flags &= CompilationFlags.EmitAsTailCallMask; switch (node.NodeType) { case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.And: case ExpressionType.ArrayIndex: case ExpressionType.Divide: case ExpressionType.Equal: case ExpressionType.ExclusiveOr: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.LeftShift: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.Modulo: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.NotEqual: case ExpressionType.Or: case ExpressionType.Power: case ExpressionType.RightShift: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.AndAlso: EmitAndAlsoBinaryExpression(node, flags); break; case ExpressionType.OrElse: EmitOrElseBinaryExpression(node, flags); break; case ExpressionType.Coalesce: EmitCoalesceBinaryExpression(node); break; case ExpressionType.Assign: EmitAssignBinaryExpression(node); break; case ExpressionType.ArrayLength: case ExpressionType.Decrement: case ExpressionType.Increment: case ExpressionType.IsFalse: case ExpressionType.IsTrue: case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.OnesComplement: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: EmitUnaryExpression(node, flags); break; case ExpressionType.Convert: case ExpressionType.ConvertChecked: EmitConvertUnaryExpression(node, flags); break; case ExpressionType.Quote: EmitQuoteUnaryExpression(node); break; case ExpressionType.Throw: EmitThrowUnaryExpression(node); break; case ExpressionType.Unbox: EmitUnboxUnaryExpression(node); break; case ExpressionType.Call: EmitMethodCallExpression(node, flags); break; case ExpressionType.Conditional: EmitConditionalExpression(node, flags); break; case ExpressionType.Constant: EmitConstantExpression(node); break; case ExpressionType.Invoke: EmitInvocationExpression(node, flags); break; case ExpressionType.Lambda: EmitLambdaExpression(node); break; case ExpressionType.ListInit: EmitListInitExpression(node); break; case ExpressionType.MemberAccess: EmitMemberExpression(node); break; case ExpressionType.MemberInit: EmitMemberInitExpression(node); break; case ExpressionType.New: EmitNewExpression(node); break; case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: EmitNewArrayExpression(node); break; case ExpressionType.Parameter: EmitParameterExpression(node); break; case ExpressionType.TypeEqual: case ExpressionType.TypeIs: EmitTypeBinaryExpression(node); break; case ExpressionType.Block: EmitBlockExpression(node, flags); break; case ExpressionType.DebugInfo: EmitDebugInfoExpression(node); break; case ExpressionType.Dynamic: EmitDynamicExpression(node); break; case ExpressionType.Default: EmitDefaultExpression(node); break; case ExpressionType.Goto: EmitGotoExpression(node, flags); break; case ExpressionType.Index: EmitIndexExpression(node); break; case ExpressionType.Label: EmitLabelExpression(node, flags); break; case ExpressionType.RuntimeVariables: EmitRuntimeVariablesExpression(node); break; case ExpressionType.Loop: EmitLoopExpression(node); break; case ExpressionType.Switch: EmitSwitchExpression(node, flags); break; case ExpressionType.Try: EmitTryExpression(node); break; default: break; } if (labelScopeChangeInfo.HasValue) { _labelBlock = labelScopeChangeInfo.Value.parent; } }
private static bool TryParseArgs(string[] args, out string sourceFile, out CompilationFlags flags) { sourceFile = null; flags = 0; if (args.Length < 1) { return(false); } foreach (string arg in args) { string normalizedArg = arg.ToUpper(); switch (normalizedArg) { case "/32": flags |= CompilationFlags.Platform32; break; case "/64": flags |= CompilationFlags.Platform64; break; case "/NODEBUG": flags |= CompilationFlags.NoDebug; break; case "/ASM": flags |= CompilationFlags.Assembly; break; default: if (normalizedArg.StartsWith("/")) { Console.WriteLine("Unrecognized option {0}", normalizedArg); return(false); } if (!File.Exists(arg)) { Console.WriteLine("Source file '{0}' not found", arg); return(false); } if (sourceFile != null) { Console.WriteLine("Multiple source files specified. Only one file is supported"); return(false); } sourceFile = arg; break; } } if (flags.HasFlag(CompilationFlags.Platform32) && flags.HasFlag(CompilationFlags.Platform64)) { Console.WriteLine("Both 32-bit and 64-bit platforms specified. Only one platform is supported"); return(false); } if (!flags.HasFlag(CompilationFlags.Platform32) && !flags.HasFlag(CompilationFlags.Platform64)) { flags |= CompilationFlags.Platform32; } if (sourceFile == null) { return(false); } // NOTE: Currently this project is always compiled for .NET Core, so this #if will always be true. But leaving // in case one wants to modify the project to run on desktop. Note that currently this compiler uses the assemblies // that it is running on as reference assemblies. So to produce .NET Framework assemblies it needs to be running // on the .NET Framework. #if NETCOREAPP flags |= CompilationFlags.NetCore; flags |= CompilationFlags.WriteDll; // force running application through dotnet #endif return(true); }
protected CompilerContext(string filePath, StreamReader inputReader, IEmitter emitter, CompilationFlags flags) : this(filePath, inputReader, new Importer(), emitter, flags) { _ownsImporter = true; }
/// <summary> /// Initializes a new instance of the PeEmitter class. /// Use this constructor to when generating an in-memory PE file /// </summary> /// <param name="flags">Compiler flags</param> public PeEmitter(CompilationFlags flags) : this(GetPathToTempFile(flags.HasFlag(CompilationFlags.WriteDll) ? "dll" : "exe"), flags) { _deleteOutputOnClose = true; }
private void EmitInvocationExpression(Expression expr, CompilationFlags flags) { InvocationExpression node = (InvocationExpression)expr; // Optimization: inline code for literal lambda's directly // // This is worth it because otherwise we end up with a extra call // to DynamicMethod.CreateDelegate, which is expensive. // if (node.LambdaOperand != null) { EmitInlinedInvoke(node, flags); return; } expr = node.Expression; if (typeof(LambdaExpression).IsAssignableFrom(expr.Type)) { // if the invoke target is a lambda expression tree, first compile it into a delegate expr = Expression.Call(expr, expr.Type.GetMethod("Compile", Array.Empty<Type>())); } expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments); EmitExpression(expr); }
private void EmitIndexAssignment(BinaryExpression node, CompilationFlags flags) { var index = (IndexExpression)node.Left; var emitAs = flags & CompilationFlags.EmitAsTypeMask; // Emit instance, if calling an instance method Type objectType = null; if (index.Object != null) { EmitInstance(index.Object, objectType = index.Object.Type); } // Emit indexes. We don't allow byref args, so no need to worry // about write-backs or EmitAddress foreach (var arg in index.Arguments) { EmitExpression(arg); } // Emit value EmitExpression(node.Right); // Save the expression value, if needed LocalBuilder temp = null; if (emitAs != CompilationFlags.EmitAsVoidType) { _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type)); } EmitSetIndexCall(index, objectType); // Restore the value if (emitAs != CompilationFlags.EmitAsVoidType) { _ilg.Emit(OpCodes.Ldloc, temp); FreeLocal(temp); } }
private void EmitMethodCallExpression(Expression expr, CompilationFlags flags = CompilationFlags.EmitAsNoTail) { var node = (MethodCallExpression)expr; EmitMethodCall(node.Object, node.Method, node, flags); }
// assumes 'object' of non-static call is already on stack private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType, CompilationFlags flags) { // Emit arguments List<WriteBack> wb = EmitArguments(mi, args); // Emit the actual call OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call; if (callOp == OpCodes.Callvirt && objectType.GetTypeInfo().IsValueType) { // This automatically boxes value types if necessary. _ilg.Emit(OpCodes.Constrained, objectType); } // The method call can be a tail call if // 1) the method call is the last instruction before Ret // 2) the method does not have any ByRef parameters, refer to ECMA-335 Partition III Section 2.4. // "Verification requires that no managed pointers are passed to the method being called, since // it does not track pointers into the current frame." if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && !MethodHasByRefParameter(mi)) { _ilg.Emit(OpCodes.Tailcall); } if (mi.CallingConvention == CallingConventions.VarArgs) { int count = args.ArgumentCount; Type[] types = new Type[count]; for (int i = 0; i < count; i++) { types[i] = args.GetArgument(i).Type; } _ilg.EmitCall(callOp, mi, types); } else { _ilg.Emit(callOp, mi); } // Emit write-backs for properties passed as "ref" arguments EmitWriteBack(wb); }
private void EmitMethodCall(Expression?obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags = CompilationFlags.EmitAsNoTail) { // Emit instance, if calling an instance method Type?objectType = null; if (!method.IsStatic) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } EmitInstance(obj, out objectType); } // if the obj has a value type, its address is passed to the method call so we cannot destroy the // stack by emitting a tail call if (obj?.Type.IsValueType == true) { EmitMethodCall(method, methodCallExpr, objectType); } else { EmitMethodCall(method, methodCallExpr, objectType, flags); } }
private DebugCompilerContext(Importer importer, StreamReader inputReader, CompilationFlags flags) : base("fake.iris", inputReader, importer, new PeEmitter(flags), flags) { }
private bool TryEmitHashtableSwitch(SwitchExpression node, CompilationFlags flags) { // If we have a comparison other than string equality, bail MethodInfo equality = String_op_Equality_String_String; if (equality != null && !equality.IsStatic) { equality = null; } if (node.Comparison != equality) { return(false); } // All test values must be constant. int tests = 0; foreach (SwitchCase c in node.Cases) { foreach (Expression t in c.TestValues) { if (!(t is ConstantExpression)) { return(false); } tests++; } } // Must have >= 7 labels for it to be worth it. if (tests < 7) { return(false); } // If we're in a DynamicMethod, we could just build the dictionary // immediately. But that would cause the two code paths to be more // different than they really need to be. var initializers = new List <ElementInit>(tests); var cases = new ArrayBuilder <SwitchCase>(node.Cases.Count); int nullCase = -1; MethodInfo add = DictionaryOfStringInt32_Add_String_Int32; for (int i = 0, n = node.Cases.Count; i < n; i++) { foreach (ConstantExpression t in node.Cases[i].TestValues) { if (t.Value != null) { initializers.Add(Expression.ElementInit(add, new TrueReadOnlyCollection <Expression>(t, Utils.Constant(i)))); } else { nullCase = i; } } cases.UncheckedAdd(Expression.SwitchCase(node.Cases[i].Body, new TrueReadOnlyCollection <Expression>(Utils.Constant(i)))); } // Create the field to hold the lazily initialized dictionary MemberExpression dictField = CreateLazyInitializedField <Dictionary <string, int> >("dictionarySwitch"); // If we happen to initialize it twice (multithreaded case), it's // not the end of the world. The C# compiler does better here by // emitting a volatile access to the field. Expression dictInit = Expression.Condition( Expression.Equal(dictField, Expression.Constant(null, dictField.Type)), Expression.Assign( dictField, Expression.ListInit( Expression.New( DictionaryOfStringInt32_Ctor_Int32, new TrueReadOnlyCollection <Expression>( Utils.Constant(initializers.Count) ) ), initializers ) ), dictField ); // // Create a tree like: // // switchValue = switchValueExpression; // if (switchValue == null) { // switchIndex = nullCase; // } else { // if (_dictField == null) { // _dictField = new Dictionary<string, int>(count) { { ... }, ... }; // } // if (!_dictField.TryGetValue(switchValue, out switchIndex)) { // switchIndex = -1; // } // } // switch (switchIndex) { // case 0: ... // case 1: ... // ... // default: // } // ParameterExpression switchValue = Expression.Variable(typeof(string), "switchValue"); ParameterExpression switchIndex = Expression.Variable(typeof(int), "switchIndex"); BlockExpression reduced = Expression.Block( new TrueReadOnlyCollection <ParameterExpression>(switchIndex, switchValue), new TrueReadOnlyCollection <Expression>( Expression.Assign(switchValue, node.SwitchValue), Expression.IfThenElse( Expression.Equal(switchValue, Expression.Constant(null, typeof(string))), Expression.Assign(switchIndex, Utils.Constant(nullCase)), Expression.IfThenElse( Expression.Call(dictInit, "TryGetValue", null, switchValue, switchIndex), Utils.Empty, Expression.Assign(switchIndex, Utils.Constant(-1)) ) ), Expression.Switch(node.Type, switchIndex, node.DefaultBody, null, cases.ToReadOnly()) ) ); EmitExpression(reduced, flags); return(true); }
/// <summary> /// Update the flag with a new EmitExpressionStart flag /// </summary> private static CompilationFlags UpdateEmitExpressionStartFlag(CompilationFlags flags, CompilationFlags newValue) { Debug.Assert(newValue == CompilationFlags.EmitExpressionStart || newValue == CompilationFlags.EmitNoExpressionStart); CompilationFlags oldValue = flags & CompilationFlags.EmitExpressionStartMask; return(flags ^ oldValue | newValue); }
private void EmitSwitchCases(SwitchExpression node, Label[] labels, bool[] isGoto, Label @default, Label end, CompilationFlags flags) { // Jump to default (to handle the fallthrough case) _ilg.Emit(OpCodes.Br, @default); // Emit the cases for (int i = 0, n = node.Cases.Count; i < n; i++) { // If the body is a goto, we already emitted an optimized // branch directly to it. No need to emit anything else. if (isGoto[i]) { continue; } _ilg.MarkLabel(labels[i]); EmitExpressionAsType(node.Cases[i].Body, node.Type, flags); // Last case doesn't need branch if (node.DefaultBody != null || i < n - 1) { if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail) { //The switch case is at the tail of the lambda so //it is safe to emit a Ret. _ilg.Emit(OpCodes.Ret); } else { _ilg.Emit(OpCodes.Br, end); } } } // Default value if (node.DefaultBody != null) { _ilg.MarkLabel(@default); EmitExpressionAsType(node.DefaultBody, node.Type, flags); } _ilg.MarkLabel(end); }
private void EmitSwitchExpression(Expression expr, CompilationFlags flags) { SwitchExpression node = (SwitchExpression)expr; if (node.Cases.Count == 0) { // Emit the switch value in case it has side-effects, but as void // since the value is ignored. EmitExpressionAsVoid(node.SwitchValue); // Now if there is a default body, it happens unconditionally. if (node.DefaultBody != null) { EmitExpressionAsType(node.DefaultBody, node.Type, flags); } else { // If there are no cases and no default then the type must be void. // Assert that earlier validation caught any exceptions to that. Debug.Assert(node.Type == typeof(void)); } return; } // Try to emit it as an IL switch. Works for integer types. if (TryEmitSwitchInstruction(node, flags)) { return; } // Try to emit as a hashtable lookup. Works for strings. if (TryEmitHashtableSwitch(node, flags)) { return; } // // Fall back to a series of tests. We need to IL gen instead of // transform the tree to avoid stack overflow on a big switch. // var switchValue = Expression.Parameter(node.SwitchValue.Type, "switchValue"); var testValue = Expression.Parameter(GetTestValueType(node), "testValue"); _scope.AddLocal(this, switchValue); _scope.AddLocal(this, testValue); EmitExpression(node.SwitchValue); _scope.EmitSet(switchValue); // Emit tests var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; for (int i = 0, n = node.Cases.Count; i < n; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (Expression test in node.Cases[i].TestValues) { // Pull the test out into a temp so it runs on the same // stack as the switch. This simplifies spilling. EmitExpression(test); _scope.EmitSet(testValue); Debug.Assert(TypeUtils.AreReferenceAssignable(testValue.Type, test.Type)); EmitExpressionAndBranch(true, Expression.Equal(switchValue, testValue, false, node.Comparison), labels[i]); } } // Define labels Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the case and default bodies EmitSwitchCases(node, labels, isGoto, @default, end, flags); }
private bool TryEmitHashtableSwitch(SwitchExpression node, CompilationFlags flags) { // If we have a comparison other than string equality, bail if (node.Comparison != typeof(string).GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static | BindingFlags.ExactBinding, null, new[] { typeof(string), typeof(string) }, null)) { return(false); } // All test values must be constant. int tests = 0; foreach (SwitchCase c in node.Cases) { foreach (Expression t in c.TestValues) { if (!(t is ConstantExpression)) { return(false); } tests++; } } // Must have >= 7 labels for it to be worth it. if (tests < 7) { return(false); } // If we're in a DynamicMethod, we could just build the dictionary // immediately. But that would cause the two code paths to be more // different than they really need to be. var initializers = new List <ElementInit>(tests); var cases = new List <SwitchCase>(node.Cases.Count); int nullCase = -1; MethodInfo add = typeof(Dictionary <string, int>).GetMethod("Add", new[] { typeof(string), typeof(int) }); for (int i = 0, n = node.Cases.Count; i < n; i++) { foreach (ConstantExpression t in node.Cases[i].TestValues) { if (t.Value != null) { initializers.Add(Expression.ElementInit(add, t, Expression.Constant(i))); } else { nullCase = i; } } cases.Add(Expression.SwitchCase(node.Cases[i].Body, Expression.Constant(i))); } // Create the field to hold the lazily initialized dictionary MemberExpression dictField = CreateLazyInitializedField <Dictionary <string, int> >("dictionarySwitch"); // If we happen to initialize it twice (multithreaded case), it's // not the end of the world. The C# compiler does better here by // emitting a volatile access to the field. Expression dictInit = Expression.Condition( Expression.Equal(dictField, Expression.Constant(null, dictField.Type)), Expression.Assign( dictField, Expression.ListInit( Expression.New( typeof(Dictionary <string, int>).GetConstructor(new[] { typeof(int) }), Expression.Constant(initializers.Count) ), initializers ) ), dictField ); // // Create a tree like: // // switchValue = switchValueExpression; // if (switchValue == null) { // switchIndex = nullCase; // } else { // if (_dictField == null) { // _dictField = new Dictionary<string, int>(count) { { ... }, ... }; // } // if (!_dictField.TryGetValue(switchValue, out switchIndex)) { // switchIndex = -1; // } // } // switch (switchIndex) { // case 0: ... // case 1: ... // ... // default: // } // var switchValue = Expression.Variable(typeof(string), "switchValue"); var switchIndex = Expression.Variable(typeof(int), "switchIndex"); var reduced = Expression.Block( new[] { switchIndex, switchValue }, Expression.Assign(switchValue, node.SwitchValue), Expression.IfThenElse( Expression.Equal(switchValue, Expression.Constant(null, typeof(string))), Expression.Assign(switchIndex, Expression.Constant(nullCase)), Expression.IfThenElse( Expression.Call(dictInit, "TryGetValue", null, switchValue, switchIndex), Expression.Empty(), Expression.Assign(switchIndex, Expression.Constant(-1)) ) ), Expression.Switch(node.Type, switchIndex, node.DefaultBody, null, cases) ); EmitExpression(reduced, flags); return(true); }
// Tries to emit switch as a jmp table private bool TryEmitSwitchInstruction(SwitchExpression node, CompilationFlags flags) { // If we have a comparison, bail if (node.Comparison != null) { return(false); } // Make sure the switch value type and the right side type // are types we can optimize Type type = node.SwitchValue.Type; if (!CanOptimizeSwitchType(type) || !TypeUtils.AreEquivalent(type, node.Cases[0].TestValues[0].Type)) { return(false); } // Make sure all test values are constant, or we can't emit the // jump table. if (!node.Cases.All(c => c.TestValues.All(t => t is ConstantExpression))) { return(false); } // // We can emit the optimized switch, let's do it. // // Build target labels, collect keys. var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; var uniqueKeys = new HashSet <decimal>(); var keys = new List <SwitchLabel>(); for (int i = 0; i < node.Cases.Count; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (ConstantExpression test in node.Cases[i].TestValues) { // Guaranteed to work thanks to CanOptimizeSwitchType. // // Use decimal because it can hold Int64 or UInt64 without // precision loss or signed/unsigned conversions. decimal key = ConvertSwitchValue(test.Value); // Only add each key once. If it appears twice, it's // allowed, but can't be reached. if (uniqueKeys.Add(key)) { keys.Add(new SwitchLabel(key, test.Value, labels[i])); } } } // Sort the keys, and group them into buckets. keys.Sort((x, y) => Math.Sign(x.Key - y.Key)); var buckets = new List <List <SwitchLabel> >(); foreach (var key in keys) { AddToBuckets(buckets, key); } // Emit the switchValue LocalBuilder value = GetLocal(node.SwitchValue.Type); EmitExpression(node.SwitchValue); _ilg.Emit(OpCodes.Stloc, value); // Create end label, and default label if needed Label end = _ilg.DefineLabel(); Label @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the switch var info = new SwitchInfo(node, value, @default); EmitSwitchBuckets(info, buckets, 0, buckets.Count - 1); // Emit the case bodies and default EmitSwitchCases(node, labels, isGoto, @default, end, flags); FreeLocal(value); return(true); }
private void EmitExpression(Expression node, CompilationFlags flags) { Debug.Assert(node != null); bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart; CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart; // only pass tail call flags to emit the expression flags = flags & CompilationFlags.EmitAsTailCallMask; switch (node.NodeType) { #region Generated Expression Compiler // *** BEGIN GENERATED CODE *** // generated by function: gen_compiler from: generate_tree.py case ExpressionType.Add: EmitBinaryExpression(node, flags); break; case ExpressionType.AddChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.And: EmitBinaryExpression(node, flags); break; case ExpressionType.AndAlso: EmitAndAlsoBinaryExpression(node, flags); break; case ExpressionType.ArrayLength: EmitUnaryExpression(node, flags); break; case ExpressionType.ArrayIndex: EmitBinaryExpression(node, flags); break; case ExpressionType.Call: EmitMethodCallExpression(node, flags); break; case ExpressionType.Coalesce: EmitCoalesceBinaryExpression(node); break; case ExpressionType.Conditional: EmitConditionalExpression(node, flags); break; case ExpressionType.Constant: EmitConstantExpression(node); break; case ExpressionType.Convert: EmitConvertUnaryExpression(node, flags); break; case ExpressionType.ConvertChecked: EmitConvertUnaryExpression(node, flags); break; case ExpressionType.Divide: EmitBinaryExpression(node, flags); break; case ExpressionType.Equal: EmitBinaryExpression(node, flags); break; case ExpressionType.ExclusiveOr: EmitBinaryExpression(node, flags); break; case ExpressionType.GreaterThan: EmitBinaryExpression(node, flags); break; case ExpressionType.GreaterThanOrEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.Invoke: EmitInvocationExpression(node, flags); break; case ExpressionType.Lambda: EmitLambdaExpression(node); break; case ExpressionType.LeftShift: EmitBinaryExpression(node, flags); break; case ExpressionType.LessThan: EmitBinaryExpression(node, flags); break; case ExpressionType.LessThanOrEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.ListInit: EmitListInitExpression(node); break; case ExpressionType.MemberAccess: EmitMemberExpression(node); break; case ExpressionType.MemberInit: EmitMemberInitExpression(node); break; case ExpressionType.Modulo: EmitBinaryExpression(node, flags); break; case ExpressionType.Multiply: EmitBinaryExpression(node, flags); break; case ExpressionType.MultiplyChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.Negate: EmitUnaryExpression(node, flags); break; case ExpressionType.UnaryPlus: EmitUnaryExpression(node, flags); break; case ExpressionType.NegateChecked: EmitUnaryExpression(node, flags); break; case ExpressionType.New: EmitNewExpression(node); break; case ExpressionType.NewArrayInit: EmitNewArrayExpression(node); break; case ExpressionType.NewArrayBounds: EmitNewArrayExpression(node); break; case ExpressionType.Not: EmitUnaryExpression(node, flags); break; case ExpressionType.NotEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.Or: EmitBinaryExpression(node, flags); break; case ExpressionType.OrElse: EmitOrElseBinaryExpression(node, flags); break; case ExpressionType.Parameter: EmitParameterExpression(node); break; case ExpressionType.Power: EmitBinaryExpression(node, flags); break; case ExpressionType.Quote: EmitQuoteUnaryExpression(node); break; case ExpressionType.RightShift: EmitBinaryExpression(node, flags); break; case ExpressionType.Subtract: EmitBinaryExpression(node, flags); break; case ExpressionType.SubtractChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.TypeAs: EmitUnaryExpression(node, flags); break; case ExpressionType.TypeIs: EmitTypeBinaryExpression(node); break; case ExpressionType.Assign: EmitAssignBinaryExpression(node); break; case ExpressionType.Block: EmitBlockExpression(node, flags); break; case ExpressionType.DebugInfo: EmitDebugInfoExpression(node); break; case ExpressionType.Decrement: EmitUnaryExpression(node, flags); break; case ExpressionType.Dynamic: EmitDynamicExpression(node); break; case ExpressionType.Default: EmitDefaultExpression(node); break; case ExpressionType.Extension: EmitExtensionExpression(node); break; case ExpressionType.Goto: EmitGotoExpression(node, flags); break; case ExpressionType.Increment: EmitUnaryExpression(node, flags); break; case ExpressionType.Index: EmitIndexExpression(node); break; case ExpressionType.Label: EmitLabelExpression(node, flags); break; case ExpressionType.RuntimeVariables: EmitRuntimeVariablesExpression(node); break; case ExpressionType.Loop: EmitLoopExpression(node); break; case ExpressionType.Switch: EmitSwitchExpression(node, flags); break; case ExpressionType.Throw: EmitThrowUnaryExpression(node); break; case ExpressionType.Try: EmitTryExpression(node); break; case ExpressionType.Unbox: EmitUnboxUnaryExpression(node); break; case ExpressionType.TypeEqual: EmitTypeBinaryExpression(node); break; case ExpressionType.OnesComplement: EmitUnaryExpression(node, flags); break; case ExpressionType.IsTrue: EmitUnaryExpression(node, flags); break; case ExpressionType.IsFalse: EmitUnaryExpression(node, flags); break; // *** END GENERATED CODE *** #endregion default: throw ContractUtils.Unreachable; } if (emitStart) { EmitExpressionEnd(startEmitted); } }
private void EmitAssign(BinaryExpression node, CompilationFlags emitAs) { switch (node.Left.NodeType) { case ExpressionType.Index: EmitIndexAssignment(node, emitAs); return; case ExpressionType.MemberAccess: EmitMemberAssignment(node, emitAs); return; case ExpressionType.Parameter: EmitVariableAssignment(node, emitAs); return; default: throw Error.InvalidLvalue(node.Left.NodeType); } }
public static TestCompilerContext Create(string compiland, GlobalSymbolList globals, CompilationFlags flags) { #if NETCOREAPP flags |= CompilationFlags.NetCore; #endif byte[] buffer = Encoding.Default.GetBytes(compiland); MemoryStream input = new MemoryStream(buffer); StreamReader reader = new StreamReader(input); MemoryStream output = new MemoryStream(); TextEmitter emitter = new TextEmitter(output); TestCompilerContext testContext = new TestCompilerContext(input, reader, output, emitter, flags); if (globals != null) { foreach (var symbol in globals.Items) { testContext.SymbolTable.Add(symbol.Item1, symbol.Item2, StorageClass.Global, null); } } return(testContext); }
private void EmitExpressionEnd(CompilationFlags flags) { if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) { PopLabelBlock(_labelBlock.Kind); } }
private void EmitUnaryExpression(Expression expr, CompilationFlags flags) { EmitUnary((UnaryExpression)expr, flags); }
private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) { var lambda = invoke.LambdaOperand; // This is tricky: we need to emit the arguments outside of the // scope, but set them inside the scope. Fortunately, using the IL // stack it is entirely doable. // 1. Emit invoke arguments List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke); // 2. Create the nested LambdaCompiler var inner = new LambdaCompiler(this, lambda, invoke); // 3. Emit the body // if the inlined lambda is the last expression of the whole lambda, // tail call can be applied. if (wb.Count != 0) { flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail); } inner.EmitLambdaBody(_scope, true, flags); // 4. Emit write-backs if needed EmitWriteBack(wb); }
private void EmitConvert(UnaryExpression node, CompilationFlags flags) { if (node.Method != null) { // User-defined conversions are only lifted if both source and // destination types are value types. The C# compiler gets this wrong. // In C#, if you have an implicit conversion from int->MyClass and you // "lift" the conversion to int?->MyClass then a null int? goes to a // null MyClass. This is contrary to the specification, which states // that the correct behaviour is to unwrap the int?, throw an exception // if it is null, and then call the conversion. // // We cannot fix this in C# but there is no reason why we need to // propagate this behavior into the expression tree API. Unfortunately // this means that when the C# compiler generates the lambda // (int? i)=>(MyClass)i, we will get different results for converting // that lambda to a delegate directly and converting that lambda to // an expression tree and then compiling it. We can live with this // discrepancy however. if (node.IsLifted && (!node.Type.IsValueType || !node.Operand.Type.IsValueType)) { var pis = node.Method.GetParameters(); Debug.Assert(pis.Length == 1); var paramType = pis[0].ParameterType; if (paramType.IsByRef) { paramType = paramType.GetElementType(); } var operand = Expression.Convert(node.Operand, paramType); Debug.Assert(operand.Method == null); node = Expression.Convert(Expression.Call(node.Method, operand), node.Type); Debug.Assert(node.Method == null); } else { EmitUnaryMethod(node, flags); return; } } if (node.Type == typeof(void)) { EmitExpressionAsVoid(node.Operand, flags); } else { if (TypeUtils.AreEquivalent(node.Operand.Type, node.Type)) { EmitExpression(node.Operand, flags); } else { // A conversion is emitted after emitting the operand, no tail call is emitted EmitExpression(node.Operand); IL.EmitConvertToType(node.Operand.Type, node.Type, node.NodeType == ExpressionType.ConvertChecked, this); } } }
/// <summary> /// Update the flag with a new EmitAsTailCall flag /// </summary> private static CompilationFlags UpdateEmitAsTailCallFlag(CompilationFlags flags, CompilationFlags newValue) { Debug.Assert(newValue == CompilationFlags.EmitAsTail || newValue == CompilationFlags.EmitAsMiddle || newValue == CompilationFlags.EmitAsNoTail); var oldValue = flags & CompilationFlags.EmitAsTailCallMask; return flags ^ oldValue | newValue; }
/// <summary> /// Update the flag with a new EmitAsTailCall flag /// </summary> private static CompilationFlags UpdateEmitAsTailCallFlag(CompilationFlags flags, CompilationFlags newValue) { Debug.Assert(newValue == CompilationFlags.EmitAsTail || newValue == CompilationFlags.EmitAsMiddle || newValue == CompilationFlags.EmitAsNoTail); CompilationFlags oldValue = flags & CompilationFlags.EmitAsTailCallMask; return(flags ^ oldValue | newValue); }
private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags) { // Emit instance, if calling an instance method Type objectType = null; if (!method.IsStatic) { EmitInstance(obj, objectType = obj.Type); } // if the obj has a value type, its address is passed to the method call so we cannot destroy the // stack by emitting a tail call if (obj != null && obj.Type.GetTypeInfo().IsValueType) { EmitMethodCall(method, methodCallExpr, objectType); } else { EmitMethodCall(method, methodCallExpr, objectType, flags); } }
/// <summary> /// Update the flag with a new EmitExpressionStart flag /// </summary> private static CompilationFlags UpdateEmitExpressionStartFlag(CompilationFlags flags, CompilationFlags newValue) { Debug.Assert(newValue == CompilationFlags.EmitExpressionStart || newValue == CompilationFlags.EmitNoExpressionStart); var oldValue = flags & CompilationFlags.EmitExpressionStartMask; return flags ^ oldValue | newValue; }
private void EmitMethodCall(Expression?obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags) { // Emit instance, if calling an instance method Type?objectType = null; if (!method.IsStatic) { Debug.Assert(obj != null); EmitInstance(obj, out objectType); } // if the obj has a value type, its address is passed to the method call so we cannot destroy the // stack by emitting a tail call if (obj != null && obj.Type.IsValueType) { EmitMethodCall(method, methodCallExpr, objectType); } else { EmitMethodCall(method, methodCallExpr, objectType, flags); } }
private void EmitVariableAssignment(BinaryExpression node, CompilationFlags flags) { var variable = (ParameterExpression)node.Left; var emitAs = flags & CompilationFlags.EmitAsTypeMask; EmitExpression(node.Right); if (emitAs != CompilationFlags.EmitAsVoidType) { _ilg.Emit(OpCodes.Dup); } if (variable.IsByRef) { // Note: the stloc/ldloc pattern is a bit suboptimal, but it // saves us from having to spill stack when assigning to a // byref parameter. We already make this same trade-off for // hoisted variables, see ElementStorage.EmitStore LocalBuilder value = GetLocal(variable.Type); _ilg.Emit(OpCodes.Stloc, value); _scope.EmitGet(variable); _ilg.Emit(OpCodes.Ldloc, value); FreeLocal(value); _ilg.EmitStoreValueIndirect(variable.Type); } else { _scope.EmitSet(variable); } }
private void EmitExpression(Expression node, CompilationFlags flags) { // When compling deep trees, we run the risk of triggering a terminating StackOverflowException, // so we use the StackGuard utility here to probe for sufficient stack and continue the work on // another thread when we run out of stack space. if (!_guard.TryEnterOnCurrentStack()) { _guard.RunOnEmptyStack((LambdaCompiler @this, Expression n, CompilationFlags f) => @this.EmitExpression(n, f), this, node, flags); return; } Debug.Assert(node != null); bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart; CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart; // only pass tail call flags to emit the expression flags = flags & CompilationFlags.EmitAsTailCallMask; switch (node.NodeType) { #region Generated Expression Compiler // *** BEGIN GENERATED CODE *** // generated by function: gen_compiler from: generate_tree.py case ExpressionType.Add: EmitBinaryExpression(node, flags); break; case ExpressionType.AddChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.And: EmitBinaryExpression(node, flags); break; case ExpressionType.AndAlso: EmitAndAlsoBinaryExpression(node, flags); break; case ExpressionType.ArrayLength: EmitUnaryExpression(node, flags); break; case ExpressionType.ArrayIndex: EmitBinaryExpression(node, flags); break; case ExpressionType.Call: EmitMethodCallExpression(node, flags); break; case ExpressionType.Coalesce: EmitCoalesceBinaryExpression(node); break; case ExpressionType.Conditional: EmitConditionalExpression(node, flags); break; case ExpressionType.Constant: EmitConstantExpression(node); break; case ExpressionType.Convert: EmitConvertUnaryExpression(node, flags); break; case ExpressionType.ConvertChecked: EmitConvertUnaryExpression(node, flags); break; case ExpressionType.Divide: EmitBinaryExpression(node, flags); break; case ExpressionType.Equal: EmitBinaryExpression(node, flags); break; case ExpressionType.ExclusiveOr: EmitBinaryExpression(node, flags); break; case ExpressionType.GreaterThan: EmitBinaryExpression(node, flags); break; case ExpressionType.GreaterThanOrEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.Invoke: EmitInvocationExpression(node, flags); break; case ExpressionType.Lambda: EmitLambdaExpression(node); break; case ExpressionType.LeftShift: EmitBinaryExpression(node, flags); break; case ExpressionType.LessThan: EmitBinaryExpression(node, flags); break; case ExpressionType.LessThanOrEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.ListInit: EmitListInitExpression(node); break; case ExpressionType.MemberAccess: EmitMemberExpression(node); break; case ExpressionType.MemberInit: EmitMemberInitExpression(node); break; case ExpressionType.Modulo: EmitBinaryExpression(node, flags); break; case ExpressionType.Multiply: EmitBinaryExpression(node, flags); break; case ExpressionType.MultiplyChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.Negate: EmitUnaryExpression(node, flags); break; case ExpressionType.UnaryPlus: EmitUnaryExpression(node, flags); break; case ExpressionType.NegateChecked: EmitUnaryExpression(node, flags); break; case ExpressionType.New: EmitNewExpression(node); break; case ExpressionType.NewArrayInit: EmitNewArrayExpression(node); break; case ExpressionType.NewArrayBounds: EmitNewArrayExpression(node); break; case ExpressionType.Not: EmitUnaryExpression(node, flags); break; case ExpressionType.NotEqual: EmitBinaryExpression(node, flags); break; case ExpressionType.Or: EmitBinaryExpression(node, flags); break; case ExpressionType.OrElse: EmitOrElseBinaryExpression(node, flags); break; case ExpressionType.Parameter: EmitParameterExpression(node); break; case ExpressionType.Power: EmitBinaryExpression(node, flags); break; case ExpressionType.Quote: EmitQuoteUnaryExpression(node); break; case ExpressionType.RightShift: EmitBinaryExpression(node, flags); break; case ExpressionType.Subtract: EmitBinaryExpression(node, flags); break; case ExpressionType.SubtractChecked: EmitBinaryExpression(node, flags); break; case ExpressionType.TypeAs: EmitUnaryExpression(node, flags); break; case ExpressionType.TypeIs: EmitTypeBinaryExpression(node); break; case ExpressionType.Assign: EmitAssignBinaryExpression(node); break; case ExpressionType.Block: EmitBlockExpression(node, flags); break; case ExpressionType.DebugInfo: EmitDebugInfoExpression(node); break; case ExpressionType.Decrement: EmitUnaryExpression(node, flags); break; case ExpressionType.Dynamic: EmitDynamicExpression(node); break; case ExpressionType.Default: EmitDefaultExpression(node); break; case ExpressionType.Extension: EmitExtensionExpression(node); break; case ExpressionType.Goto: EmitGotoExpression(node, flags); break; case ExpressionType.Increment: EmitUnaryExpression(node, flags); break; case ExpressionType.Index: EmitIndexExpression(node); break; case ExpressionType.Label: EmitLabelExpression(node, flags); break; case ExpressionType.RuntimeVariables: EmitRuntimeVariablesExpression(node); break; case ExpressionType.Loop: EmitLoopExpression(node); break; case ExpressionType.Switch: EmitSwitchExpression(node, flags); break; case ExpressionType.Throw: EmitThrowUnaryExpression(node); break; case ExpressionType.Try: EmitTryExpression(node); break; case ExpressionType.Unbox: EmitUnboxUnaryExpression(node); break; case ExpressionType.TypeEqual: EmitTypeBinaryExpression(node); break; case ExpressionType.OnesComplement: EmitUnaryExpression(node, flags); break; case ExpressionType.IsTrue: EmitUnaryExpression(node, flags); break; case ExpressionType.IsFalse: EmitUnaryExpression(node, flags); break; // *** END GENERATED CODE *** #endregion default: throw ContractUtils.Unreachable; } if (emitStart) { EmitExpressionEnd(startEmitted); } }
private void EmitMemberAssignment(BinaryExpression node, CompilationFlags flags) { MemberExpression lvalue = (MemberExpression)node.Left; MemberInfo member = lvalue.Member; // emit "this", if any Type objectType = null; if (lvalue.Expression != null) { EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type); } // emit value EmitExpression(node.Right); LocalBuilder temp = null; var emitAs = flags & CompilationFlags.EmitAsTypeMask; if (emitAs != CompilationFlags.EmitAsVoidType) { // save the value so we can return it _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type)); } var fld = member as FieldInfo; if ((object)fld != null) { _ilg.EmitFieldSet((FieldInfo)member); } else { var prop = member as PropertyInfo; if ((object)prop != null) { EmitCall(objectType, prop.GetSetMethod(true)); } else { throw Error.InvalidMemberType(member); } } if (emitAs != CompilationFlags.EmitAsVoidType) { _ilg.Emit(OpCodes.Ldloc, temp); FreeLocal(temp); } }
private static bool TryParseArgs(string[] args, out string sourceFile, out CompilationFlags flags) { sourceFile = null; flags = 0; if (args.Length < 1) { return(false); } foreach (string arg in args) { string normalizedArg = arg.ToUpper(); switch (normalizedArg) { case "/32": flags |= CompilationFlags.Platform32; break; case "/64": flags |= CompilationFlags.Platform64; break; case "/NODEBUG": flags |= CompilationFlags.NoDebug; break; case "/ASM": flags |= CompilationFlags.Assembly; break; default: if (normalizedArg.StartsWith("/")) { Console.WriteLine("Unrecognized option {0}", normalizedArg); return(false); } if (!File.Exists(arg)) { Console.WriteLine("Source file '{0}' not found", arg); return(false); } if (sourceFile != null) { Console.WriteLine("Multiple source files specified. Only one file is supported"); return(false); } sourceFile = arg; break; } } if (flags.HasFlag(CompilationFlags.Platform32) && flags.HasFlag(CompilationFlags.Platform64)) { Console.WriteLine("Both 32-bit and 64-bit platforms specified. Only one platform is supported"); return(false); } if (!flags.HasFlag(CompilationFlags.Platform32) && !flags.HasFlag(CompilationFlags.Platform64)) { flags |= CompilationFlags.Platform32; } if (sourceFile == null) { return(false); } return(true); }