public override Expression GenDlr(GenContext context) { Expression basicBody = _tryExpr.GenDlr(context); // Wrap the basic body, a Comma, in a return to a label //LabelTarget target = Expression.Label(basicBody.Type, "ret_label"); //Expression tryBody = Expression.Return(target, basicBody); Expression tryBody = Expression.Convert(basicBody,typeof(object)); CatchBlock[] catches = new CatchBlock[_catchExprs.count()]; for ( int i=0; i<_catchExprs.count(); i++ ) { CatchClause clause = (CatchClause) _catchExprs.nth(i); ParameterExpression parmExpr = Expression.Parameter(clause.Type, clause.Lb.Name); clause.Lb.ParamExpression = parmExpr; catches[i] = Expression.Catch(parmExpr, Expression.Convert(clause.Handler.GenDlr(context), typeof(object))); } TryExpression tryStmt = _finallyExpr == null ? Expression.TryCatch(tryBody, catches) : Expression.TryCatchFinally(tryBody, _finallyExpr.GenDlr(context), catches); //Expression defaultValue = Expression.Default(basicBody.Type); //Expression whole = Expression.Block(tryStmt, Expression.Label(target, defaultValue)); //return whole; return tryStmt; }
public override void VisitCFGCatchBlock(CatchBlock x) { VisitCFGBlockInit(x); // add catch control variable to the state Accept(x.Variable); VisitTypeRef(x.TypeRef); State.SetLocalType(State.GetLocalHandle(x.Variable.Name.NameValue.Value), TypeCtx.GetTypeMask(x.TypeRef.TypeRef)); // x.Variable.ResultType = x.TypeRef.ResolvedType; // VisitCFGBlockInternal(x); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { CompilerScope scope; if (node.Variable == null) { this.Visit(node.Body); return(node); } this._tree.Scopes[node] = scope = new CompilerScope(node, false); this._scopes.Push(scope); this.Visit(node.Body); this._scopes.Pop(); return(node); }
public void SettingValues() { var sut = new CatchBlock { Kind = CatchBlockKind.General, Parameter = SomeParameter(), Body = { new ReturnStatement() } }; Assert.AreEqual(SomeParameter(), sut.Parameter); Assert.AreEqual(Lists.NewList(new ReturnStatement()), sut.Body); Assert.AreEqual(CatchBlockKind.General, sut.Kind); }
protected virtual bool CompareCatchBlock(CatchBlock node1, CatchBlock node2, object state) { if (AreBothNull(node1, node2)) { return(true); } if (AreEitherNull(node1, node2)) { return(false); } return(CompareType(node1.Test, node2.Test, state) && Compare(node1.Body, node2.Body, state) && CompareParameter(node1.Variable, node2.Variable, state) && Compare(node1.Filter, node2.Filter, state)); }
/// <inheritdoc/> protected override CatchBlock VisitCatchBlock(CatchBlock node) { Debug.Assert(node != null); var expected = PeekExpectedExpressionNode <CatchBlock>(); if (!CheckNotNull(expected)) { return(node); } if (!CheckEqual(node.Test, expected.Test)) { return(node); } PopExpectedExpressionNode(); return(base.VisitCatchBlock(node)); }
protected void VisitCatchBlock(CatchBlock node, TState variableState, TState filterState, TState bodyState, bool visitVariable, bool visitFilter, bool visitBody) { ValidateArguments(node); if (visitVariable && node.Variable != null) { Visit(Context.Create(node, variableState), node.Variable); } if (visitFilter && node.Filter != null) { Visit(Context.Create(node, filterState), node.Filter); } if (visitBody && node.Body != null) { Visit(Context.Create(node, bodyState), node.Body); } }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node.Variable != null) { PushParameters(new[] { node.Variable }); } Visit(node.Body); Visit(node.Filter); if (node.Variable != null) { PopParameters(new[] { node.Variable }); } return(node); }
protected virtual bool CompareCatchBlock(CatchBlock a, CatchBlock b) { if (a.Variable != null && b.Variable != null) { if (a.Variable.Type != b.Variable.Type) { return(false); } this.parameterScope.Add(a.Variable, b.Variable); } return(a.Test == b.Test && this.Compare(a.Body, b.Body) && this.Compare(a.Filter, b.Filter) && this.Compare(a.Variable, b.Variable)); }
private static string GetCatchBlock(CatchBlock catchBlock, TranslationContext context) { var catchBody = context.TranslateCodeBlock(catchBlock.Body); var exceptionClause = GetExceptionClause(catchBlock, context); var catchBodyBlock = catchBody.WithCurlyBraces(); if (catchBlock.Variable != null) { catchBodyBlock = catchBodyBlock .Replace($"throw {catchBlock.Variable.Name};", "throw;"); } return($@"catch{exceptionClause}{catchBodyBlock} "); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } var vistedNode = base.VisitCatchBlock(node); // Testing the parent expression is only required if all children are evaluatable if (IsCurrentSubtreeEvaluatable) { IsCurrentSubtreeEvaluatable = EvaluatableExpressionFilter.IsEvaluatableCatchBlock(node); } return(vistedNode); }
private static string GetExceptionClause(CatchBlock catchBlock, TranslationContext context) { var exceptionTypeName = catchBlock.Test.GetFriendlyName(); if (ExceptionUsageFinder.IsVariableUsed(catchBlock)) { var filter = (catchBlock.Filter != null) ? " when " + context.Translate(catchBlock.Filter) : null; return($" ({exceptionTypeName} {catchBlock.Variable.Name})" + filter); } return((catchBlock.Test != typeof(Exception)) ? $" ({exceptionTypeName})" : null); }
public override void VisitCFGCatchBlock(CatchBlock x) { VisitCFGBlockInit(x); // add catch control variable to the state x.TypeRef.Accept(this); x.Variable.Access = BoundAccess.Write.WithWrite(TypeCtx.GetTypeMask(x.TypeRef.TypeRef)); State.SetLocalType(State.GetLocalHandle(x.Variable.Name.NameValue), x.Variable.Access.WriteMask); Accept(x.Variable); // x.Variable.ResultType = x.TypeRef.ResolvedType; // VisitCFGBlockInternal(x); }
static void Main(string[] args) { Loader.Load(); Objects.LoadType(typeof(DynamicLinqTestObjects)); var lines = new List <string>(); foreach (var(key, filename) in rendererFileMapping.SelectKVP((k, v) => (k, v))) { var ordering = parseFileOrder(@$ "C:\Users\Spitz\source\repos\zspitz\ExpressionTreeToString\Tests\expectedResults\{filename}-testdata.txt"); var language = key == VisualBasic ? Language.VisualBasic : Language.CSharp; const string dlinq = nameof(DynamicLinqTestObjects); var objects = Objects.Get() .Where(x => key == DynamicLinq ? x.source == dlinq : x.source != dlinq) .Where(x => !ordering.ContainsKey($"{x.source}.{x.name}")); //var objects = Objects.Get() // .Where(x => key == DynamicLinq ? x.source == dlinq : x.source != dlinq) // .OrderBy(x => ordering.TryGetValue($"{x.source}.{x.name}", out var order) ? order : -1); foreach (var(category, source, name, o) in objects) { var toWrite = o switch { Expression expr => expr.ToString(key, out var pathSpans, language), MemberBinding mbind => mbind.ToString(key, out var pathSpans, language), ElementInit init => init.ToString(key, out var pathSpans, language), SwitchCase switchCase => switchCase.ToString(key, out var pathSpans, language), CatchBlock catchBlock => catchBlock.ToString(key, out var pathSpans, language), LabelTarget labelTarget => labelTarget.ToString(key, out var pathSpans, language), _ => throw new NotImplementedException(), }; lines.Add($"---- {source}.{name}"); if (key == FactoryMethods) { toWrite = toWrite.Replace(@"// using static System.Linq.Expressions.Expression ", ""); } lines.Add(toWrite); } lines.Add("------"); }
protected override CatchBlock VisitCatchBlock(CatchBlock node, Type expectedType) { if (evaluator.EnsureKnownType( node.Test, genericArgumentsUpdated: updatedType => node = Expression.MakeCatchBlock( updatedType, VisitAndConvert(node.Variable, "VisitCatchBlock-Variable", null), Visit(node.Body, expectedType), Visit(node.Filter, typeof(bool))))) { return(node); } else { return(base.VisitCatchBlock(node, expectedType)); } }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node.Filter != null) { Visit(node.Body); _forbidden.Push(nameof(CatchBlock)); { Visit(node.Filter); } _forbidden.Pop(); return(node); } return(base.VisitCatchBlock(node)); }
public CatchBlockTranslation(CatchBlock catchBlock, ITranslationContext context) { _catchBodyTranslation = GetBlockTranslation(catchBlock.Body, context); _exceptionClause = GetExceptionClauseOrNullFor(catchBlock, context); if ((_catchBodyTranslation.NodeType != ExpressionType.Throw) && catchBlock.Body.IsReturnable()) { _catchBodyTranslation.WithReturnKeyword(); } EstimatedSize = _catchBodyTranslation.EstimatedSize; if (_exceptionClause != null) { EstimatedSize += _exceptionClause.EstimatedSize; } }
private int DoAppendCatchString(StringBuilder builder, CatchBlock block, int indent) { builder.Append(' ', 3 * indent); builder.Append("catch "); builder.Append(block.CatchType.ToString()); builder.Append(Environment.NewLine); ++indent; int index = block.Index; while (index < block.Index + block.Length) { index += DoAppendInstructionString(builder, index, indent); } --indent; return(block.Length); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node.Variable == null) { Visit(node.Filter); Visit(node.Body); return(node); } var createdScope = new CompilerScope(node, false); _tree.Scopes[node] = createdScope; _scopes.Push(createdScope); Visit(node.Filter); Visit(node.Body); _scopes.Pop(); return(node); }
private CatchBlockDto GetCatchBlock(CatchBlock obj) { if (this.catchBlocks.ContainsKey(obj)) { return(this.catchBlocks[obj]); } var res = new CatchBlockDto { Body = Visit(obj.Body), Filter = Visit(obj.Filter), Test = obj.Test, Variable = Visit((Expression)obj.Variable) as ParameterExpressionDto }; this.catchBlocks.Add(obj, res); return(res); }
public static bool IsVariableUsed(CatchBlock catchHandler) { if (catchHandler.Variable == null) { return(false); } var visitor = new ExceptionUsageFinder(catchHandler); visitor.Visit(catchHandler.Filter); if (!visitor._usageFound) { visitor.Visit(catchHandler.Body); } return(visitor._usageFound); }
public override void VisitTryStatement(ITryStatement block, IList <IStatement> body) { AddIf(block, CompletionCase.EmptyCompletionBefore, body); var tryBlock = new TryBlock(); body.Add(tryBlock); AddIf(block, CompletionCase.InBody, tryBlock.Body); AddIf(block, CompletionCase.InFinally, tryBlock.Finally); VisitBlock(block.Try, tryBlock.Body); VisitBlock(block.FinallyBlock, tryBlock.Finally); foreach (var clause in block.Catches) { var catchBlock = new CatchBlock(); tryBlock.CatchBlocks.Add(catchBlock); AddIf(clause, CompletionCase.InBody, catchBlock.Body); VisitBlock(clause.Body, catchBlock.Body); var generalClause = clause as IGeneralCatchClause; if (generalClause != null) { catchBlock.Kind = CatchBlockKind.General; continue; } var specificClause = clause as ISpecificCatchClause; if (specificClause != null) { var varDecl = specificClause.ExceptionDeclaration; var isUnnamed = varDecl == null; var typeName = specificClause.ExceptionType.GetName(); var varName = isUnnamed ? "?" : varDecl.DeclaredName; catchBlock.Parameter = Names.Parameter("[{0}] {1}", typeName, varName); catchBlock.Kind = isUnnamed ? CatchBlockKind.Unnamed : CatchBlockKind.Default; } } AddIf(block, CompletionCase.EmptyCompletionAfter, body); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node.Variable != null) { this._shadowedVars.Push(new HashSet <ParameterExpression>(new ParameterExpression[] { node.Variable })); } Expression body = this.Visit(node.Body); Expression filter = this.Visit(node.Filter); if (node.Variable != null) { this._shadowedVars.Pop(); } if ((body == node.Body) && (filter == node.Filter)) { return(node); } return(Expression.MakeCatchBlock(node.Test, node.Variable, body, filter)); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { Out(Flow.NewLine, "} .Catch (" + node.Test.ToString()); if (node.Variable != null) { Out(Flow.Space, ""); VisitParameter(node.Variable); } if (node.Filter != null) { Out(") .If (", Flow.Break); Visit(node.Filter); } Out(") {", Flow.NewLine); Indent(); Visit(node.Body); Dedent(); return(node); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { if (node.Variable != null) { _shadowedVars.Push(new Set <ParameterExpression>(new[] { node.Variable })); } Expression b = Visit(node.Body); Expression f = Visit(node.Filter); if (node.Variable != null) { _shadowedVars.Pop(); } if (b == node.Body && f == node.Filter) { return(node); } return(Expression.MakeCatchBlock(node.Test, node.Variable, b, f)); }
private TryExpression DeserializeTry() { var flags = _stream.ReadByte(); var type = default(Type); if ((flags & Protocol.TRY_HASTYPE) == Protocol.TRY_HASTYPE) { type = DeserializeType(); } var body = Deserialize(); var handlers = default(CatchBlock[]); if ((flags & Protocol.TRY_HASCATCH) == Protocol.TRY_HASCATCH) { var n = (int)_stream.ReadUInt32Compact(); handlers = new CatchBlock[n]; for (var i = 0; i < n; i++) { handlers[i] = DeserializeCatchBlock(); } } var fault = default(Expression); if ((flags & Protocol.TRY_HASFAULT) == Protocol.TRY_HASFAULT) { fault = Deserialize(); } var @finally = default(Expression); if ((flags & Protocol.TRY_HASFINALLY) == Protocol.TRY_HASFINALLY) { @finally = Deserialize(); } return(_parent._factory.MakeTry(type, body, @finally, fault, handlers)); }
static ManagedWrapper() { // Create sub-expressions which don't depend on the interface type. This saves a bit of resources. paramNativeObject = Expression.Parameter(typeof(IntPtr), "pNative"); Type tException = typeof(Exception); MethodInfo miExceptionHresult = tException.GetProperty("HResult").GetGetMethod(); // Create return target and catch block, they don't depend on the interface nor the input object returnTarget = Expression.Label(typeof(int)); var eException = Expression.Parameter(typeof(Exception), "ex"); var eCatchBody = Expression.Return(returnTarget, Expression.Property(eException, miExceptionHresult)); exprCatchBlock = Expression.Catch(eException, eCatchBody); // Same for pointer-returning methods pointerReturnTarget = Expression.Label(typeof(IntPtr)); eCatchBody = Expression.Return(pointerReturnTarget, MiscUtils.nullptr); exprPointerCatchBlock = Expression.Catch(eException, eCatchBody); }
/// <summary> /// Returns an enumerator that iterates through the CatchBlock. /// </summary> /// <param name="catchBlock">The CatchBlock to iterate.</param> /// <returns>An enumerator that can be used to iterate through the CatchBlock.</returns> /// <exception cref="ArgumentNullException">The CatchBlock is null.</exception> protected virtual IEnumerable <Expression> CatchBlockIterator([DisallowNull] CatchBlock catchBlock) { if (catchBlock.Variable != null) { yield return(catchBlock.Variable); } if (catchBlock.Filter != null) { foreach (var filter in Iterator(catchBlock.Filter)) { yield return(filter); } } foreach (var body in Iterator(catchBlock.Body)) { yield return(body); } }
private void HandleException(Node node, Exception exc) { // Searching appropriate handler Type excType = exc.GetType(); EHBlock handler = null; Block parent = node.Parent; while (!(parent is MethodBodyBlock)) { if (parent is ProtectedBlock) { ProtectedBlock tryBlock = parent as ProtectedBlock; for (int count = 0; count < tryBlock.Count && handler == null; count++) { if (tryBlock[count] is CatchBlock) { CatchBlock catchBlock = tryBlock[count] as CatchBlock; if (catchBlock.Type.IsAssignableFrom(excType)) { handler = catchBlock; } } } } parent = parent.Parent; } if (handler == null) { unhandledException = exc; } else { state.Stack.Clear(); state.Stack.Push(new ObjectReferenceValue(exc)); AddTask(handler); } }
private void EmitCatchStart(CatchBlock cb) { if (cb.Filter == null) { EmitSaveExceptionOrPop(cb); return; } // emit filter block. Filter blocks are untyped so we need to do // the type check ourselves. var endFilter = IL.DefineLabel(); var rightType = IL.DefineLabel(); // skip if it's not our exception type, but save // the exception if it is so it's available to the // filter IL.Emit(OpCodes.Isinst, cb.Test); IL.Emit(OpCodes.Dup); IL.Emit(OpCodes.Brtrue, rightType); IL.Emit(OpCodes.Pop); IL.Emit(OpCodes.Ldc_I4_0); IL.Emit(OpCodes.Br, endFilter); // it's our type, save it and emit the filter. IL.MarkLabel(rightType); EmitSaveExceptionOrPop(cb); var parent = _labelBlock; _labelBlock = new LabelScopeInfo(parent, LabelScopeKind.Filter); EmitExpression(cb.Filter); _labelBlock = parent; // begin the catch, clear the exception, we've // already saved it IL.MarkLabel(endFilter); IL.BeginCatchBlock(null); IL.Emit(OpCodes.Pop); }
protected override CatchBlock VisitCatchBlock(CatchBlock node) { var peek = localParameters.Peek(); var variable = node.Variable; if (variable != null && peek.Contains(variable)) { variable = null; } if (variable != null) { peek.Add(variable); } var res = base.VisitCatchBlock(node); if (variable != null) { peek.Remove(variable); } return(res); }
public Expression GenCode(RHC rhc, ObjExpr objx, GenContext context) { Expression basicBody = _tryExpr.GenCode(rhc, objx, context); if (basicBody.Type == typeof(void)) basicBody = Expression.Block(basicBody, Expression.Default(typeof(object))); Expression tryBody = Expression.Convert(basicBody,typeof(object)); CatchBlock[] catches = new CatchBlock[_catchExprs.count()]; for ( int i=0; i<_catchExprs.count(); i++ ) { CatchClause clause = (CatchClause) _catchExprs.nth(i); ParameterExpression parmExpr = Expression.Parameter(clause.Type, clause.Lb.Name); clause.Lb.ParamExpression = parmExpr; catches[i] = Expression.Catch(parmExpr, Expression.Convert(clause.Handler.GenCode(rhc, objx, context), typeof(object))); } TryExpression tryStmt = _finallyExpr == null ? Expression.TryCatch(tryBody, catches) : Expression.TryCatchFinally(tryBody, _finallyExpr.GenCode(RHC.Statement, objx, context), catches); return tryStmt; }
private int DoCheckCatch(TypedInstructionCollection instructions, CatchBlock cb, int varIndex) { int offset = -1; // Loop through all of the instructions in the catch block, for (int index = cb.Index; index < cb.Index + cb.Length && offset < 0; ++index) { TypedInstruction instruction = instructions[index]; // if we find a throw instruction, if (instruction.Untyped.OpCode.Code == Code.Throw) { // and it's preceded by a load from varIndex then we have a problem. LoadLocal load = instructions[index - 1] as LoadLocal; if (load != null && load.Variable == varIndex) { offset = instruction.Untyped.Offset; Log.DebugLine(this, "bad throw at {0:X2}", offset); } } } return offset; }
protected internal virtual void PostWalk(CatchBlock node) { }
// CatchBlock protected internal virtual bool Walk(CatchBlock node) { return true; }
/// <summary> /// Emits the start of a catch block. The exception value that is provided by the /// CLR is stored in the variable specified by the catch block or popped if no /// variable is provided. /// </summary> private void EmitCatchStart(CatchBlock cb) { if (cb.Filter != null && !IsDynamicMethod) { // emit filter block as filter. Filter blocks are // untyped so we need to do the type check ourselves. _ilg.BeginExceptFilterBlock(); Label endFilter = _ilg.DefineLabel(); Label rightType = _ilg.DefineLabel(); // skip if it's not our exception type, but save // the exception if it is so it's available to the // filter _ilg.Emit(OpCodes.Isinst, cb.Test); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brtrue, rightType); _ilg.Emit(OpCodes.Pop); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Br, endFilter); // it's our type, save it and emit the filter. _ilg.MarkLabel(rightType); EmitSaveExceptionOrPop(cb); PushLabelBlock(LabelBlockKind.Filter); EmitExpression(cb.Filter); PopLabelBlock(LabelBlockKind.Filter); // begin the catch, clear the exception, we've // already saved it _ilg.MarkLabel(endFilter); _ilg.BeginCatchBlock(null); _ilg.Emit(OpCodes.Pop); } else { _ilg.BeginCatchBlock(cb.Test); EmitSaveExceptionOrPop(cb); if (cb.Filter != null) { Label catchBlock = _ilg.DefineLabel(); // filters aren't supported in dynamic methods so instead // emit the filter as if check, if (!expr) rethrow PushLabelBlock(LabelBlockKind.Filter); EmitExpressionAndBranch(true, cb.Filter, catchBlock); PopLabelBlock(LabelBlockKind.Filter); _ilg.Emit(OpCodes.Rethrow); _ilg.MarkLabel(catchBlock); // catch body continues } } }
public virtual void VisitCFGCatchBlock(CatchBlock x) { Accept(x.Variable); VisitCFGBlockInternal(x); }
void EmitCatchBlock(CodeGenerator cg, CatchBlock catchBlock) { Debug.Assert(catchBlock.Variable.Variable != null); if (catchBlock.TypeRef.ResolvedType == null) { throw new NotImplementedException("handle exception type dynamically"); // TODO: if (ex is ctx.ResolveType(ExceptionTypeName)) { ... } } var extype = catchBlock.TypeRef.ResolvedType; cg.Builder.AdjustStack(1); // Account for exception on the stack. cg.Builder.OpenLocalScope(ScopeType.Catch, (Microsoft.Cci.ITypeReference)extype); // <tmp> = <ex> var tmploc = cg.GetTemporaryLocal(extype); cg.Builder.EmitLocalStore(tmploc); var varplace = catchBlock.Variable.BindPlace(cg); Debug.Assert(varplace != null); // $x = <tmp> varplace.EmitStorePrepare(cg); cg.Builder.EmitLocalLoad(tmploc); varplace.EmitStore(cg, (TypeSymbol)tmploc.Type); // cg.ReturnTemporaryLocal(tmploc); tmploc = null; // cg.GenerateScope(catchBlock, NextBlock.Ordinal); // cg.Builder.CloseLocalScope(); }
public override void VisitCFGCatchBlock(CatchBlock x) { VisitCFGBlockInit(x); // add catch control variable to the state Accept(x.Variable); VisitTypeRef(x.TypeRef); // x.Variable.ResultType = x.TypeRef.ResolvedType; // VisitCFGBlockInternal(x); }
internal CatchRecord(LocalBuilder local, CatchBlock block) { _local = local; _block = block; }
private void EmitSaveExceptionOrPop(CatchBlock cb) { if (cb.Variable != null) { // If the variable is present, store the exception // in the variable. _scope.EmitSet(cb.Variable); } else { // Otherwise, pop it off the stack. _ilg.Emit(OpCodes.Pop); } }
public CatchBlockProxy(CatchBlock node) { _node = node; }
/// <summary> /// Emits the start of a catch block. The exception value that is provided by the /// CLR is stored in the variable specified by the catch block or popped if no /// variable is provided. /// </summary> private void EmitCatchStart(CatchBlock cb) { if (cb.Filter == null) { EmitSaveExceptionOrPop(cb); return; } // emit filter block. Filter blocks are untyped so we need to do // the type check ourselves. Label endFilter = _ilg.DefineLabel(); Label rightType = _ilg.DefineLabel(); // skip if it's not our exception type, but save // the exception if it is so it's available to the // filter _ilg.Emit(OpCodes.Isinst, cb.Test); _ilg.Emit(OpCodes.Dup); _ilg.Emit(OpCodes.Brtrue, rightType); _ilg.Emit(OpCodes.Pop); _ilg.Emit(OpCodes.Ldc_I4_0); _ilg.Emit(OpCodes.Br, endFilter); // it's our type, save it and emit the filter. _ilg.MarkLabel(rightType); EmitSaveExceptionOrPop(cb); PushLabelBlock(LabelScopeKind.Filter); EmitExpression(cb.Filter); PopLabelBlock(LabelScopeKind.Filter); // begin the catch, clear the exception, we've // already saved it _ilg.MarkLabel(endFilter); _ilg.BeginCatchBlock(null); _ilg.Emit(OpCodes.Pop); }
/// <summary> /// Process an unplugged method. /// </summary> /// <param name="aMethod">The method to process.</param> /// <param name="staticConstructorDependencyRoot">Null if method scanning is not a static constructor. Otherwise, the root node that represents the static constructor being scanned.</param> /// <returns>A new ILChunk with ILOpInfos and common attribites loaded. Null if any errors occur.</returns> /// <exception cref="System.Exception"> /// Thrown when an unrecognised operand type is read. Can occur if MSBuild has been /// updated/extended from when the kernel compiler was last updated. /// </exception> public ILChunk ProcessUnpluggedMethod(MethodBase aMethod, StaticConstructorDependency staticConstructorDependencyRoot = null) { ILChunk result = new ILChunk() { Plugged = false, Method = aMethod }; //Pre-process common method attributes so we get information such as //whether to apply GC or not etc. ProcessCommonMethodAttributes(aMethod, result); //Get the method body which can then be used to get locals info and //IL bytes that are the IL code. MethodBody theMethodBody = aMethod.GetMethodBody(); //Method body for something like [DelegateType].Invoke() // is null if (theMethodBody == null) { //Just return empty method return result; } //For each local variable in this method foreach (LocalVariableInfo aLocal in theMethodBody.LocalVariables) { //Add it to our list of locals with some common information pre-worked out LocalVariable localItem = new LocalVariable() { sizeOnStackInBytes = Utils.GetNumBytesForType(aLocal.LocalType), isFloat = Utils.IsFloat(aLocal.LocalType), TheType = aLocal.LocalType, isGCManaged = Utils.IsGCManaged(aLocal.LocalType) }; result.LocalVariables.Add(localItem); } //Used later to store location and length of the cleanup try-finally block int CleanUpBlock_TryOffset = 0; int CleanUpBlock_TryLength = 0; int CleanUpBlock_FinallyOffset = 0; int CleanUpBlock_FinallyLength = 0; //The "cleanup" block is the finally handler created by the IL reader that //calls GC.DecrementRefCount of locals and arguments as-required so that //memory managed by the GC through objects gets freed correctly. //The try-section of the cleanup block surrounds all of the main code of the method //excluding the final "ret" instruction. In this way, even if an exception occurs, //locals and args still get "cleaned up". //The IL bytes are the IL code. byte[] ILBytes = theMethodBody.GetILAsByteArray(); //Note: IL offsets are usually calculated as the number of bytes offset from the // start of the method. //Note: IL line numbers are IL offsets. //The current position in the IL bytes. //This will change throughout the loop below so it always points past //all the bytes processed so far. int ILBytesPos = 0; //The previous position in the IL bytes. //This will only change in the loop below after a new IL op is created. In this way, //it actually points to the IL bytes position just before the new op is created. //That is to say, it points to the IL bytes pos of the start of the new op. int PrevILBytesPos = 0; //The previous IL op info that was created. //This is the latest one that was created as opposed the the one before that. //I.e. this is the last ILOpInfo added to the final list of IL op infos. ILOpInfo prevInfo = null; //Loop through all the IL bytes for this method... while (ILBytesPos < ILBytes.Length) { //The current System.Reflection.Emit.OpCode being processed OpCode currOpCode; //The unique number that identifies the op code. //This number is also deliberately equivalent to Kernel.Compiler.ILOps.IlOp.OpCodes! ushort currOpCodeID = 0; //MSIL is saved such that OpIds that only require 1 byte, only use 1 byte! //ILBytes encoded as big-endian(?) so high bytes of the op code value (ID) come first //So if high byte is set to 0xFE then we need to load the next byte as low byte if (ILBytes[ILBytesPos] == 0xFE) { currOpCodeID = (ushort)(0xFE00 + (short)ILBytes[ILBytesPos + 1]); ILBytesPos += 2; } else { currOpCodeID = (ushort)ILBytes[ILBytesPos]; ILBytesPos++; } //Load the op code from our pre-constructed list of all op codes currOpCode = AllOpCodes[currOpCodeID]; int operandSize = 0; //Operand type tells us the operand size //We must: // a) Skip over the operand bytes so that we read the next IL op correctly // b) Store the operand bytes in the ILOpInfo for later use switch(currOpCode.OperandType) { case OperandType.InlineBrTarget: operandSize = 4; break; case OperandType.InlineField: operandSize = 4; break; case OperandType.InlineI: operandSize = 4; break; case OperandType.InlineI8: operandSize = 8; break; case OperandType.InlineMethod: operandSize = 4; break; case OperandType.InlineNone: //No operands = no op size break; case OperandType.InlineR: operandSize = 8; break; case OperandType.InlineSig: operandSize = 4; break; case OperandType.InlineString: operandSize = 4; break; case OperandType.InlineSwitch: { int count = Utils.ReadInt32(ILBytes, ILBytesPos); ILBytesPos += 4; operandSize = count * 4; } break; case OperandType.InlineTok: operandSize = 4; break; case OperandType.InlineType: operandSize = 4; break; case OperandType.InlineVar: operandSize = 2; break; case OperandType.ShortInlineBrTarget: operandSize = 1; break; case OperandType.ShortInlineI: operandSize = 1; break; case OperandType.ShortInlineR: operandSize = 4; break; case OperandType.ShortInlineVar: operandSize = 1; break; default: throw new Exception("Unrecognised operand type!"); } //Update the previous op with next position now that we // know what that is... if (prevInfo != null) { prevInfo.NextPosition = PrevILBytesPos; } //The IL reader pre-loads any methods that should be called by, for example, a call op //This was added so that the MethodToCall could be set by the IL reader to inject call ops // - It was going to be a lot harder to try and get the "metadata token bytes" for the // method to call than to simply "pre-load" the method to call. MethodBase methodToCall = null; //Value bytes generally contain a constant value to be loaded or the bytes of a metadata token. //Metadata tokens can be used to retrieve information such as string literals or method infos //from the calling assembly. byte[] valueBytes = new byte[operandSize]; //Don't bother copying 0 bytes... if (operandSize > 0) { //Copy the bytes... Array.Copy(ILBytes, ILBytesPos, valueBytes, 0, operandSize); //If the op is one where the valueBytes are a metadata token pointing to a method: if ((ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Call || (ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Calli || (ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Callvirt || (ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Ldftn || (ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Newobj) { //Pre-load the method for reasons described above. //The metadata token that identifies the method to call in the DLL //It is used to retrieve more information about the method from the DLL int MethodMetadataToken = Utils.ReadInt32(valueBytes, 0); //The method to call retrieved using the metasdata token methodToCall = aMethod.Module.ResolveMethod(MethodMetadataToken); } } //If the op being processed is a Return op and this method has GC applied: if ((ILOps.ILOp.OpCodes)currOpCode.Value == ILOps.ILOp.OpCodes.Ret && result.ApplyGC) { //We must insert the cleanup block code. //Insert try-finally block around the entire method but just before Ret //The finally block can then do clean-up of local variables and args //1. Insert IL ops for doing locals / args cleanup //2. Add the try/finally block (or just finally block if try block already exists) //We must also consider the fact that just before a "ret" op, the return value is loaded. //Since this cleanup block will wrap that load op, we must add code to store the return value //at the end of the try block and then re-load the return value after the finally block (but //before the ret op.) //Get a list of all the params to the current method List<Type> allParams = result.Method.GetParameters() .Select(x => x.ParameterType) .ToList(); //If it isn't a static method: if (!result.Method.IsStatic) { //The first param is the current instance reference. allParams.Insert(0, result.Method.DeclaringType); } //Only insert the cleanup block if there are some params or locals to clean up. //This is the first of two checks of this condition. if (result.LocalVariables.Count > 0 || allParams.Count > 0) { //As per above we need to check for return value LocalVariable returnValVariable = null; //Return type of constructor is void, so only check proper methods if(result.Method is MethodInfo) { Type returnType = ((MethodInfo)result.Method).ReturnType; //Void return type = no return value if(returnType != typeof(void)) { //Add a new local variable for storing the return value returnValVariable = new LocalVariable() { isFloat = Utils.IsFloat(returnType), sizeOnStackInBytes = Utils.GetNumBytesForType(returnType), TheType = returnType, isGCManaged = Utils.IsGCManaged(returnType) }; result.LocalVariables.Add(returnValVariable); //This will become the penultimate IL op of the try-block //It will immediately follow the op just before ret which // will have loaded the return value or, at the very least, // the top-most item on the stack is the return value //This op stores that return value in our new local variable // for reload after the finally block has completed. result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Stloc, Position = PrevILBytesPos++, NextPosition = PrevILBytesPos, ValueBytes = BitConverter.GetBytes(result.LocalVariables.IndexOf(returnValVariable)) }); } } //This becomes the last op of the try-block (and is required to be // the last op of a try block) result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Leave_S, Position = PrevILBytesPos++, NextPosition = PrevILBytesPos, ValueBytes = new byte[4] }); //Try block length is now the length in IL bytes from start // (i.e. offset) to start of the current IL op. See above for // why we use PrevIlBytesPos. CleanUpBlock_TryLength = PrevILBytesPos - CleanUpBlock_TryOffset; //Finally offset is offset to first op of finally block i.e. // current IL op position. CleanUpBlock_FinallyOffset = PrevILBytesPos; //Finally length is currently 0 - gets increased later. CleanUpBlock_FinallyLength = 0; //Add cleanup code for each local for (int i = 0; i < result.LocalVariables.Count; i++) { //Clean-up local variables //If the local variable is GC handled: //1. Load the the local //2. Call GC Dec Ref count LocalVariable aVar = result.LocalVariables[i]; //Only add cleanup code if the local is actually GC managed. if (Utils.IsGCManaged(aVar.TheType)) { if (prevInfo != null) { prevInfo.NextPosition = PrevILBytesPos; } //Load the local result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Ldloc, Position = PrevILBytesPos++, NextPosition = -1, ValueBytes = BitConverter.GetBytes(i) }); prevInfo.NextPosition = PrevILBytesPos; //Decrement the ref count of the local result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Call, Position = PrevILBytesPos++, NextPosition = -1, SetToGCDecRefCountMethod = true }); CleanUpBlock_FinallyLength += 2; } } //Add cleanup code for each arg //Dec ref count of all args passed to the method for (int i = 0; i < allParams.Count; i++) { Type aVarType = allParams[i]; //Only add cleanup code if the arg is actually GC managed. if (Utils.IsGCManaged(aVarType)) { if (prevInfo != null) { prevInfo.NextPosition = PrevILBytesPos; } result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Ldarg, Position = PrevILBytesPos++, NextPosition = -1, ValueBytes = BitConverter.GetBytes(i) }); prevInfo.NextPosition = PrevILBytesPos; result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Call, Position = PrevILBytesPos++, NextPosition = -1, SetToGCDecRefCountMethod = true }); CleanUpBlock_FinallyLength += 2; } } //Locals and args not necessarily of GC managed type // so we could potentially have cleaned up nothing //This is the second of the two checks to make sure we // only add cleanup code if there is something to cleanup if (CleanUpBlock_FinallyLength > 0) { //If there is cleanup code, add the end of the finally block and // reload the return value if necessary. prevInfo.NextPosition = PrevILBytesPos; result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Endfinally, Position = PrevILBytesPos++, NextPosition = -1 }); CleanUpBlock_FinallyLength += 1; if (returnValVariable != null) { result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = OpCodes.Ldloc, Position = PrevILBytesPos++, NextPosition = PrevILBytesPos, ValueBytes = BitConverter.GetBytes(result.LocalVariables.IndexOf(returnValVariable)) }); } } else { //If there was nothing to cleanup, we need to remove // the ops and locals etc. that got added earlier. result.ILOpInfos.RemoveAt(result.ILOpInfos.Count - 1); PrevILBytesPos--; if(returnValVariable != null) { result.LocalVariables.Remove(returnValVariable); result.ILOpInfos.RemoveAt(result.ILOpInfos.Count - 1); PrevILBytesPos--; } } } } if (staticConstructorDependencyRoot != null) { //Create our static constructor dependency tree //Each of these ops could try to access a static method or field switch((ILOps.ILOp.OpCodes)currOpCode.Value) { case ILOps.ILOp.OpCodes.Call: //Check if the method to call is static and not a constructor itself //If so, we must add the declaring type's static constructors to the tree { int metadataToken = Utils.ReadInt32(valueBytes, 0); MethodBase methodBaseInf = aMethod.Module.ResolveMethod(metadataToken); if(!(methodBaseInf.IsConstructor || methodBaseInf is ConstructorInfo)) { MethodInfo methodInf = (MethodInfo)methodBaseInf; ConstructorInfo[] staticConstructors = methodInf.DeclaringType.GetConstructors(BindingFlags.Static | BindingFlags.Public) .Concat(methodInf.DeclaringType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)) .ToArray(); if (staticConstructors.Length > 0) { ConstructorInfo TheConstructor = staticConstructors[0]; if (staticConstructorDependencyRoot[TheConstructor] == null) { staticConstructorDependencyRoot.Children.Add(new StaticConstructorDependency() { TheConstructor = TheConstructor }); } } } } break; case ILOps.ILOp.OpCodes.Ldsfld: case ILOps.ILOp.OpCodes.Ldsflda: case ILOps.ILOp.OpCodes.Stsfld: { int metadataToken = Utils.ReadInt32(valueBytes, 0); FieldInfo fieldInf = aMethod.Module.ResolveField(metadataToken); ConstructorInfo[] staticConstructors = fieldInf.DeclaringType.GetConstructors(BindingFlags.Static | BindingFlags.Public) .Concat(fieldInf.DeclaringType.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic)) .ToArray(); if(staticConstructors.Length > 0) { ConstructorInfo TheConstructor = staticConstructors[0]; if (staticConstructorDependencyRoot[TheConstructor] == null) { staticConstructorDependencyRoot.Children.Add(new StaticConstructorDependency() { TheConstructor = TheConstructor }); } } } break; } } //Add the IL op result.ILOpInfos.Add(prevInfo = new ILOpInfo() { opCode = currOpCode, Position = PrevILBytesPos, // Next position set to -1 indicates no next op NextPosition = -1, ValueBytes = valueBytes, MethodToCall = methodToCall }); ILBytesPos += operandSize; PrevILBytesPos = ILBytesPos; } prevInfo.NextPosition = PrevILBytesPos; //Add the exception handlers (excluding Cleanup try-finally block - see below) foreach (ExceptionHandlingClause aClause in theMethodBody.ExceptionHandlingClauses) { ExceptionHandledBlock exBlock = result.GetExactExceptionHandledBlock(aClause.TryOffset); if (exBlock == null) { exBlock = new ExceptionHandledBlock(); exBlock.Offset = aClause.TryOffset; exBlock.Length = aClause.TryLength; result.ExceptionHandledBlocks.Add(exBlock); } switch (aClause.Flags) { case ExceptionHandlingClauseOptions.Fault: case ExceptionHandlingClauseOptions.Clause: { CatchBlock catchBlock = new CatchBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength, //Though not used, we may as well set it anyway FilterType = aClause.CatchType }; exBlock.CatchBlocks.Add(catchBlock); } break; case ExceptionHandlingClauseOptions.Finally: { FinallyBlock finallyBlock = new FinallyBlock() { Offset = aClause.HandlerOffset, Length = aClause.HandlerLength }; exBlock.FinallyBlocks.Add(finallyBlock); } break; default: OutputError(new NotSupportedException("Exception handling clause not supported! Type: " + aClause.Flags.ToString())); break; } } //Add the cleanup try-finally block //Only add the block if try-section has non-zero length and // if the finally block has more than just the endfinally op if (CleanUpBlock_TryLength != 0 && CleanUpBlock_FinallyLength > 1) { ExceptionHandledBlock cleanUpTryBlock = new ExceptionHandledBlock() { Offset = CleanUpBlock_TryOffset, Length = CleanUpBlock_TryLength }; FinallyBlock cleanupFinallyBlock = new FinallyBlock() { Offset = CleanUpBlock_FinallyOffset, Length = CleanUpBlock_FinallyLength, }; cleanUpTryBlock.FinallyBlocks.Add(cleanupFinallyBlock); result.ExceptionHandledBlocks.Add(cleanUpTryBlock); } return result; }
internal TryCatchEdge(BoundBlock source, BoundBlock body, CatchBlock[] catchBlocks, BoundBlock finallyBlock, BoundBlock endBlock) : base(source) { _body = body; _catchBlocks = catchBlocks; _finallyBlock = finallyBlock; _end = endBlock; Connect(source); }
private void DoAddBlock(CatchBlock block) { DoAddBlock(block.Index, block.Length - 1); // ditto }