private void CompileTryExpression(Expression expr) { var node = (TryExpression)expr; Label startOfFinally = MakeLabel(); if (node.Finally != null) { _finallyLabels.Push(new FinallyLabels(startOfFinally)); } int startingStack = _currentStackDepth; int start = _instructions.Count; this.Compile(node.Body); int end = _instructions.Count; if (_currentStackDepth != -1) { AddBranch(startOfFinally); } if (node.Finally == null && node.Fault == null && node.Handlers.Count == 1) { var handler = node.Handlers[0]; if (handler.Filter == null && handler.Test == typeof(Exception) && handler.Variable == null) { if (EndsWithRethrow(handler.Body)) { var fault = this.AddHandler(null, null, start, end); _currentStackDepth = startingStack; CompileWithoutRethrow(handler.Body); fault.EndHandlerIndex = this._instructions.Count; startOfFinally.Mark(); return; } } } foreach (var handler in node.Handlers) { if (handler.Filter != null) throw new NotImplementedException(); var parameter = handler.Variable; // TODO we should only create one of these if needed for a rethrow if (parameter == null) { parameter = Expression.Parameter(handler.Test, "currentException"); AddVariable(parameter); } this.AddHandler(handler.Test, parameter, start, end); _exceptionForRethrowStack.Push(parameter); _currentStackDepth = startingStack + 1; SetVariable(parameter, true); this.Compile(handler.Body); //TODO pop this scoped variable that we no longer need //PopVariable(parameter); _exceptionForRethrowStack.Pop(); AddBranch(startOfFinally); } if (node.Fault != null) { throw new NotImplementedException(); } if (node.Finally != null) { var myLabels = _finallyLabels.Pop(); var myNewTargets = new List<Label>(); int finallyStart = _instructions.Count; ParameterExpression finallyStateVar = null; ParameterExpression finallyStackValue = null; foreach (var kv in myLabels.labels) { var label = kv.Key; if (label._index == -1 || label._index < start || label._index > finallyStart) { myNewTargets.Add(label); var currentLabel = MakeLabel(); _currentStackDepth = -1; currentLabel._expectedStackSize = label._expectedStackSize; currentLabel.Mark(); foreach (var branch in kv.Value) { currentLabel.SetOffset(branch); label.RemoveBinding(branch); } if (finallyStateVar == null) { finallyStateVar = Expression.Parameter(typeof(int), "finallyBranch"); AddVariable(finallyStateVar); finallyStackValue = Expression.Parameter(typeof(object), "stackValue"); AddVariable(finallyStackValue); } if (myLabels.labelHasValue[label]) { SetVariable(finallyStackValue, true); } PushConstant(myNewTargets.Count-1); SetVariable(finallyStateVar, true); AddBranch(startOfFinally); } } _currentStackDepth = startingStack + ((node.Body.Type == typeof(void)) ? 0 : 1); startOfFinally.Mark(); var faultHandler = this.AddHandler(null, null, start, end); this.Compile(node.Finally); if (node.Finally.Type != typeof(void)) { AddInstruction(PopInstruction.Instance); } faultHandler.EndHandlerIndex = _instructions.Count; if (finallyStateVar != null) { // we can make this much more efficient in the future var si = new SwitchInstruction(); AddInstruction(GetVariable(finallyStateVar)); int switchIndex = _instructions.Count; AddInstruction(si); int switchStack = _currentStackDepth; for (int i = 0; i < myNewTargets.Count; i++) { _currentStackDepth = switchStack; si.AddCase(i, _instructions.Count-switchIndex); if (myLabels.labelHasValue[myNewTargets[i]]) { AddInstruction(GetVariable(finallyStackValue)); } var branchInstruction = AddBranch(myNewTargets[i]); if (_finallyLabels.Count > 0) { var labels = _finallyLabels.Peek(); labels.AddBranch(branchInstruction, myNewTargets[i], myLabels.labelHasValue[myNewTargets[i]]); } } si.AddDefault(_instructions.Count - switchIndex); _currentStackDepth = switchStack; // we might exit totally normally! } } else { startOfFinally.Mark(); } }
private void CompileSwitchExpression(Expression expr) { var node = (SwitchExpression)expr; if (node.Test.Type != typeof(int)) throw new NotImplementedException(); Debug.Assert(node.Type == typeof(void)); this.Compile(node.Test); int start = _instructions.Count; var switchInstruction = new SwitchInstruction(); AddInstruction(switchInstruction); bool setDefault = false; int switchStack = _currentStackDepth; foreach (var clause in node.SwitchCases) { _currentStackDepth = switchStack; int offset = _instructions.Count - start; if (clause.IsDefault) { setDefault = true; switchInstruction.AddDefault(offset); } else { switchInstruction.AddCase(clause.Value, offset); } this.Compile(clause.Body); Debug.Assert(_currentStackDepth == -1 || _currentStackDepth == switchStack); } if (!setDefault) { switchInstruction.AddDefault(_instructions.Count - start); _currentStackDepth = switchStack; } if (node.BreakLabel != null) { ReferenceLabel(node.BreakLabel).Mark(); } }