private void BindMap <T> (ISeq <JST.Statement> statements, IMap <T, int> usageMap, IMap <T, JST.Expression> boundMap, Func <T, string> mkName, Func <T, JST.Expression> mkExpression) { foreach (var kv in usageMap) { if (!boundMap.ContainsKey(kv.Key)) { if (kv.Value > 1) { var e = mkExpression(kv.Key); if (e != null) { if (env.DebugMode) { statements.Add(new JST.CommentStatement(mkName(kv.Key))); } var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); boundMap.Add(kv.Key, id.ToE()); } } // else: inline expression as need it } // else: use outer binding } }
public void BindUsage(ISeq <JST.Statement> statements, CST.Usage usage, TypePhase typePhase) { foreach (var kv in usage.Assemblies) { if (kv.Value > 1) { if (!boundAssemblies.ContainsKey(kv.Key)) { var e = env.JSTHelpers.DefaultResolveAssembly(this, kv.Key); if (e != null) { if (env.DebugMode) { statements.Add(new JST.CommentStatement(kv.Key.ToString())); } var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); boundAssemblies.Add(kv.Key, id.ToE()); } } // else: use outer binding } // else: inline expression as need it } foreach (var kv in usage.Types) { if (kv.Value > 1) { var existing = default(ExpressionAndPhase); var b = boundTypes.TryGetValue(kv.Key, out existing); if (!b || typePhase > existing.Phase) { var e = env.JSTHelpers.DefaultResolveType(this, kv.Key, typePhase); if (e != null) { if (env.DebugMode) { statements.Add(new JST.CommentStatement(kv.Key.ToString())); } var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); var updated = new ExpressionAndPhase(id.ToE(), typePhase); if (b) { boundTypes[kv.Key] = updated; } else { boundTypes.Add(kv.Key, updated); } } } // else: use outer binding } // else: inline expression as need it } }
// Collect types we need to compile private void CollectTypes() { foreach (var typeDef in assmEnv.Assembly.Types.Where(t => t.Invalid == null)) { var tyconEnv = assmEnv.AddType(typeDef); if (typeDef.IsModule) { // Look for any <Module>::.cctor() // We know both <Module> and the .cctor have no type parameters var methodDef = typeDef.Members.OfType <CST.MethodDef>().Where (m => m.Invalid == null && m.IsStatic && m.IsConstructor).FirstOrDefault(); if (methodDef != null) { moduleInitializer = methodDef.SelfMethodReference(tyconEnv); } } if (typeDef.IsUsed) { typeDefs.Add(typeDef); } else { if (assemblyTrace == null || assemblyTrace.IncludeAssembly) { Env.Log(new UnusedDefinitionMessage(CST.MessageContextBuilders.Env(tyconEnv))); } } } }
private void CoalesceFrom(BasicBlock origHead, Set <BasicBlock> group, BasicBlock newBlock, Set <BasicBlock> visited) { if (!visited.Contains(this)) { visited.Add(this); FixupTargets(origHead, newBlock); if (Equals(newBlock)) { foreach (var s in origHead.Sources) { if (!group.Contains(s)) { newBlock.Sources.Add(s); } } } else { for (var i = 0; i < Sources.Count; i++) { if (group.Contains(Sources[i])) { Sources.RemoveAt(i--); } } } if (newBlock.Targets.Contains(this)) { Sources.Add(newBlock); } foreach (var t in Targets) { if (group.Contains(t)) { throw new InvalidOperationException ("existing basic block has target strictly within group being removed"); } t.CoalesceFrom(origHead, group, newBlock, visited); } } }
private static void PreOrderFrom(BasicBlock bb, ISeq <BasicBlock> res, Set <BasicBlock> visited) { if (!visited.Contains(bb)) { visited.Add(bb); res.Add(bb); foreach (var t in bb.Targets) { PreOrderFrom(t, res, visited); } } }
private static void AllEdgesFrom(BasicBlock bb, ISeq <BBEdge> edges, Set <BasicBlock> visited) { if (!visited.Contains(bb)) { visited.Add(bb); foreach (var t in bb.Targets) { var edge = new BBEdge { Source = bb, Target = t }; edges.Add(edge); AllEdgesFrom(t, edges, visited); } } }
private ExpressionStackEntry Eval(ISeq <Statement> statements, ExpressionStackEntry entry) { if (statements == null) { throw new ArgumentNullException("statements"); } // Eval the expression and save it in a temporary. If result is a structure, make the implied // value semantics explicit by cloning the value. var id = gensym(); var cell = new VariableCell(id); compEnv.AddVariable(id, ArgLocal.Local, false, true, compEnv.SubstituteType(entry.Expression.Type(compEnv))); statements.Add(new ExpressionStatement(cell.Write(entry.Expression.CloneIfStruct(compEnv)))); // We never re-write to temporaries, so this stack entry now has no effects, not even a 'read' effect. // Also, the result's Expression.IsValue will be true. return(new ExpressionStackEntry(cell.Read(), bottom)); }
private Effects PeekAndEval(ISeq <Statement> statements, int arity, bool isLinear, Effects bodyEffects) { if (arity > Depth) { return(Failed(default(Effects), "underflow in peek-and-eval")); } // We may need to evaluate some of the arguments var argEvalStatements = default(Seq <Statement>); var argAndBodyEffects = default(Effects); var deferredEffects = default(Effects); if (isLinear) { argAndBodyEffects = bodyEffects; deferredEffects = ArgEffects(arity); } else { argEvalStatements = new Seq <Statement>(); argAndBodyEffects = ProtectStackFromNonLinearEvaluation(argEvalStatements, arity, bodyEffects, out deferredEffects); } // We may need to evaluate some entries in the stack below the arguments if (!ProtectStackFromEffects(statements, argAndBodyEffects, arity)) { return(default(Effects)); } if (argEvalStatements != null) { // Any effects of these stataments have already been accumulated above foreach (var s in argEvalStatements) { if (statements == null) { return(Failed(default(Effects), "eval in peek-and-eval")); } statements.Add(s); } } return(deferredEffects); }
private bool Dump(ISeq <Statement> statements, MachineState state, int skip, int i) { if (statements == null) { return(Failed(false, "eval in flush")); } var id = state.PeekId(stack.Count - (1 + skip) - i, gensym); var cell = new VariableCell(id); var read = stack[i].Expression as ReadExpression; if (read == null || !read.Address.Equals(cell.AddressOf())) { // Ok if id already added as temporary compEnv.AddVariable(id, ArgLocal.Local, false, true, compEnv.SubstituteType(state.PeekType(stack.Count - (1 + skip) - i))); statements.Add(new ExpressionStatement(cell.Write(stack[i].Expression.CloneIfStruct(compEnv)))); } // else: stack entry has not changed, so no need to save it return(true); }
public override void AccumLeaveTrys(IMSet <BasicBlock> visited, ISeq <LeaveTryBasicBlock> acc, int depth) { if (!visited.Contains(this)) { visited.Add(this); if (depth == 0) { // Only include leave's from the try we started from, not inner trys acc.Add(this); } else { depth -= HandlerPopCount; if (depth >= 0) { Target.AccumLeaveTrys(visited, acc, depth); } } } }
private void ConsumeCommentStatement(ISeq<Statement> statements) { if (pendingComments != null) { statements.Add(new CommentStatement(pendingCommentsLoc, pendingComments.ToString())); pendingComments = null; pendingCommentsLoc = null; } }
public void BindUsage(ISeq<JST.Statement> statements, CST.Usage usage, TypePhase typePhase) { foreach (var kv in usage.Assemblies) { if (kv.Value > 1) { if (!boundAssemblies.ContainsKey(kv.Key)) { var e = env.JSTHelpers.DefaultResolveAssembly(this, kv.Key); if (e != null) { if (env.DebugMode) statements.Add(new JST.CommentStatement(kv.Key.ToString())); var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); boundAssemblies.Add(kv.Key, id.ToE()); } } // else: use outer binding } // else: inline expression as need it } foreach (var kv in usage.Types) { if (kv.Value > 1) { var existing = default(ExpressionAndPhase); var b = boundTypes.TryGetValue(kv.Key, out existing); if (!b || typePhase > existing.Phase) { var e = env.JSTHelpers.DefaultResolveType(this, kv.Key, typePhase); if (e != null) { if (env.DebugMode) statements.Add(new JST.CommentStatement(kv.Key.ToString())); var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); var updated = new ExpressionAndPhase(id.ToE(), typePhase); if (b) boundTypes[kv.Key] = updated; else boundTypes.Add(kv.Key, updated); } } // else: use outer binding } // else: inline expression as need it } }
private static void AllEdgesFrom(BasicBlock bb, ISeq<BBEdge> edges, Set<BasicBlock> visited) { if (!visited.Contains(bb)) { visited.Add(bb); foreach (var t in bb.Targets) { var edge = new BBEdge { Source = bb, Target = t }; edges.Add(edge); AllEdgesFrom(t, edges, visited); } } }
protected void Lex(ISeq <string> args, TextReader reader) { var sb = new StringBuilder(); while (true) { var c = reader.Read(); if (c < 0) { return; } if (c == '"') { while (true) { var d = reader.Read(); if (d < 0) { // Ignore non-terminated string... args.Add(sb.ToString()); return; } else if (d == '"') { var e = reader.Read(); if (e < 0) { args.Add(sb.ToString()); return; } else if (e == '"') { sb.Append((char)e); } else { args.Add(sb.ToString()); sb = new StringBuilder(); break; } } else { sb.Append((char)d); } } } else if (!IsWS(c)) { sb.Append((char)c); while (true) { var d = reader.Read(); if (d < 0) { args.Add(sb.ToString()); return; } else if (IsWS(d)) { args.Add(sb.ToString()); sb = new StringBuilder(); break; } else { sb.Append((char)d); } } } // else: discard } }
// Take acccount of: // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private JST.Expression AppendFinalImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments) { var isInstanceMethod = !(methodDef.IsStatic || methodDef.IsConstructor); var scriptExpectsRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref scriptExpectsRoot); var passInstAsArg = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref passInstAsArg); var instanceIsThis = isInstanceMethod && !passInstAsArg; var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var funcScript = script as JST.FunctionExpression; var nextArg = 0; var instArg = default(JST.Expression); if (instanceIsThis) { // Instance argument will be the first arg to 'call' or 'apply', or the target of a '.' call. instArg = arguments[nextArg++]; if (lastArgIsParamsArray && !instArg.IsDuplicatable) { // Make sure instance argument is evaluated before the remaining arguments var instId = nameSupply.GenSym(); body.Add(JST.Statement.Var(instId, instArg)); instArg = instId.ToE(); } } else { if (lastArgIsParamsArray) instArg = new JST.NullExpression(); } var knownArgs = 0; var call = default(JST.Expression); if (lastArgIsParamsArray) { // We mush build script args at runtime var argsId = nameSupply.GenSym(); body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral())); if (scriptExpectsRoot) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE())); knownArgs++; } while (nextArg < arguments.Count - 1) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, arguments[nextArg++])); knownArgs++; } var arrArg = arguments[nextArg]; if (!arrArg.IsDuplicatable) { var arrId = nameSupply.GenSym(); body.Add(JST.Statement.Var(arrId, arrArg)); arrArg = arrId.ToE(); } var iId = nameSupply.GenSym(); body.Add (new JST.IfStatement (JST.Expression.IsNotNull(arrArg), new JST.Statements (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(0), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(arrArg, Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (argsId.ToE(), Constants.push, new JST.IndexExpression(arrArg, iId.ToE()))))))); if (funcScript != null) { // var args = ...; var x = script; x.apply(this/null, args) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.apply, instArg, argsId.ToE()); } else { if (instanceIsThis) { // var args = ...; (this.script).apply(this, args); call = JST.Expression.DotCall (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), Constants.apply, instArg, argsId.ToE()); } else { // var args = ...; script.apply(null, args) call = JST.Expression.DotCall(script, Constants.apply, instArg, argsId.ToE()); } } } else { var callArgs = new Seq<JST.Expression>(); if (instanceIsThis && funcScript != null) callArgs.Add(instArg); if (scriptExpectsRoot) { callArgs.Add(rootId.ToE()); knownArgs++; } while (nextArg < arguments.Count) { callArgs.Add(arguments[nextArg++]); knownArgs++; } if (instanceIsThis) { if (funcScript != null) { // var x = script; x.call(this, arg1, ..., argn) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.call, callArgs); } else // this.script(arg1, ..., angn) call = new JST.CallExpression (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), callArgs); } else // script(arg1, ..., argn) call = new JST.CallExpression(script, callArgs); } if (funcScript != null) { if (funcScript.Parameters.Count < knownArgs) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); env.Log(new InvalidInteropMessage(ctxt, "script accepts too few arguments")); throw new DefinitionException(); } } return call; }
private void EnsurePathExists(ISeq<JST.Statement> statements, JST.Expression script, bool isStatic) { var path = JST.Expression.ExplodePath(script); for (var i = isStatic ? 0 : 1; i < path.Count - 1; i++) { var prefixPath = new Seq<JST.PropertyName>(); for (var j = 0; j <= i; j++) prefixPath.Add(path[j]); var prefix = JST.Expression.Path(prefixPath); if (i == 0) { var exId = new JST.Identifier("e"); #if !JSCRIPT_IS_CORRECT statements.Add(JST.Statement.Var(exId)); #endif statements.Add (new JST.TryStatement (new JST.Statements(new JST.ExpressionStatement(prefix)), new JST.CatchClause (exId, new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral()))))); } else if (!path[i].Value.Equals(Constants.prototype.Value, StringComparison.Ordinal)) statements.Add (new JST.IfStatement (JST.Expression.IsNull(prefix), new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral())))); } }
// ---------------------------------------------------------------------- // Entry point from TypeCompiler // ---------------------------------------------------------------------- public void Emit(ISeq<JST.Statement> body, JST.Expression target) { if (env.BreakOnBreak && env.AttributeHelper.MethodHasAttribute(methEnv.Assembly, methEnv.Type, methEnv.Method, env.AttributeHelper.BreakAttributeRef, false, false)) System.Diagnostics.Debugger.Break(); var methodName = CST.CSTWriter.WithAppend(env.Global, CST.WriterStyle.Debug, methEnv.MethodRef.Append); var methodSlot = env.GlobalMapping.ResolveMethodDefToSlot(methEnv.Assembly, methEnv.Type, methEnv.Method); var method = Method(methodName); switch (mode) { case MethodCompilationMode.SelfContained: { if (target != null) throw new InvalidOperationException("not expecting target in self-contained mode"); var assmName = CST.CSTWriter.WithAppend (env.Global, CST.WriterStyle.Uniform, methEnv.Assembly.Name.Append); var typeSlot = env.GlobalMapping.ResolveTypeDefToSlot(methEnv.Assembly, methEnv.Type); var func = new JST.FunctionExpression (new Seq<JST.Identifier> { rootId, assemblyId, typeDefinitionId }, new JST.Statements(new JST.ReturnStatement(method))); var methodLoader = new Seq<JST.Statement>(); if (env.DebugMode) methodLoader.Add(new JST.CommentStatement(methodName)); methodLoader.Add (JST.Statement.DotCall (new JST.Identifier(env.Root).ToE(), Constants.RootBindMethod, new JST.StringLiteral(assmName), new JST.StringLiteral(typeSlot), new JST.BooleanLiteral(env.InteropManager.IsStatic(methEnv.Assembly, methEnv.Type, methEnv.Method)), new JST.StringLiteral(methodSlot), func)); var methodProgram = new JST.Program(new JST.Statements(methodLoader)); var methodFileName = Path.Combine (env.OutputDirectory, Path.Combine (JST.Lexemes.StringToFileName(assmName), Path.Combine(typeSlot, Path.Combine(methodSlot, Constants.MethodFileName)))); methodProgram.ToFile(methodFileName, env.PrettyPrint); env.Log(new GeneratedJavaScriptFile("method '" + methEnv.MethodRef + "'", methodFileName)); break; } case MethodCompilationMode.DirectBind: { if (target == null) throw new InvalidOperationException("expecting target in self-contained mode"); if (env.DebugMode) body.Add(new JST.CommentStatement(methodName)); body.Add (JST.Statement.Assignment(JST.Expression.Dot(target, new JST.Identifier(methodSlot)), method)); break; } default: throw new ArgumentOutOfRangeException(); } }
// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private void AppendFinalExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, JST.Expression instance, ISeq<JST.Statement> body, AppendCallExported appendCallExported) { if (script == null) throw new InvalidOperationException("expecting default script value"); var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); var isInstance = !methodDef.IsStatic && !methodDef.IsConstructor; if (isInstance) { if (typeDef.Style is CST.ValueTypeStyle) { env.Log(new InvalidInteropMessage(ctxt, "cannot export instance methods from value types")); throw new DefinitionException(); } } var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var isPassRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref isPassRoot); var isProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref isProto); var isPassInstance = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref isPassInstance); var bindToInstance = isInstance && !isProto; if (bindToInstance != (instance != null)) throw new InvalidOperationException("expecting instance"); var captureThis = isInstance && !isPassInstance; var funcScript = script as JST.FunctionExpression; // Build the function to export var funcBody = new Seq<JST.Statement>(); var funcParameters = new Seq<JST.Identifier>(); var funcCallArgs = new Seq<JST.Expression>(); var funcArity = methodDef.Arity; if ((methodDef.IsConstructor && !methodDef.IsStatic) || captureThis) // unmanaged will not pass the instance funcArity--; if (lastArgIsParamsArray) // managed params args will be extracted from remainder of unmanaged arguments funcArity--; if (captureThis) funcCallArgs.Add(new JST.ThisExpression()); for (var i = 0; i < funcArity; i++) { var id = nameSupply.GenSym(); funcParameters.Add(id); funcCallArgs.Add(id.ToE()); } if (lastArgIsParamsArray) { var iId = nameSupply.GenSym(); var arrId = nameSupply.GenSym(); funcBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); funcBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(funcArity), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(Constants.arguments.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (arrId.ToE(), Constants.push, new JST.IndexExpression(Constants.arguments.ToE(), iId.ToE()))))); funcCallArgs.Add(arrId.ToE()); } appendCallExported(nameSupply, assemblyDef, typeDef, methodDef, funcBody, funcCallArgs); var func = new JST.FunctionExpression(funcParameters, new JST.Statements(funcBody)); // Export the above function if (funcScript != null) { var scriptArgs = new Seq<JST.Expression>(); if (isPassRoot) scriptArgs.Add(rootId.ToE()); if (bindToInstance) scriptArgs.Add(instance); scriptArgs.Add(func); if (funcScript.Parameters.Count != scriptArgs.Count) { env.Log(new InvalidInteropMessage(ctxt, "invalid function arity")); throw new DefinitionException(); } body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, scriptArgs))); } else { if (bindToInstance) script = JST.Expression.Dot(instance, JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, func)); } }
// ---------------------------------------------------------------------- // Object helper methods in type structure // ---------------------------------------------------------------------- private void AccumInstanceFields(CST.TypeEnvironment thisTypeEnv, ISeq<CST.FieldRef> fields) { if (thisTypeEnv.Type.Extends != null) AccumInstanceFields(thisTypeEnv.Type.Extends.Enter(thisTypeEnv), fields); foreach (var fieldDef in thisTypeEnv.Type.Members.OfType<CST.FieldDef>().Where (f => f.Invalid == null && f.IsUsed && !f.IsStatic)) fields.Add(new CST.FieldRef(thisTypeEnv.TypeRef, fieldDef.FieldSignature)); }
private static void PreOrderFrom(BasicBlock bb, ISeq<BasicBlock> res, Set<BasicBlock> visited) { if (!visited.Contains(bb)) { visited.Add(bb); res.Add(bb); foreach (var t in bb.Targets) PreOrderFrom(t, res, visited); } }
// ---------------------------------------------------------------------- // Effective control flow (accounting for exceptions) // ---------------------------------------------------------------------- // Given block is within a try. What instructions within it may originate an exception? private void AddExceptionalExits(ISeq<Instruction> exits, Instructions block) { foreach (var instruction in block.Body) { if (instruction.Flavor == InstructionFlavor.Try) { var tryi = (TryInstruction)instruction; var addedBody = false; foreach (var handler in tryi.Handlers) { if (handler.Flavor == HandlerFlavor.Filter) throw new InvalidOperationException("filter block"); // An exception handler may always throw an exception of its own AddExceptionalExits(exits, handler.Body); if (handler.Flavor == HandlerFlavor.Catch) { // Its possible for the catch handler not to match the throw exception, // thus exception will escape from try body if (!addedBody) { AddExceptionalExits(exits, tryi.Body); addedBody = true; } } // else: Fault and Finally blocks always capture the exception, and continue with // their own control flow. Thus no exception will escape the try body directly. } } else exits.Add(instruction); } }