internal void Reference(LabelScopeInfo block) { _references.Add(block); if (_definitions.Count > 0) { ValidateJump(block); } }
private void CheckTry() { // Try inside a filter is not verifiable for (LabelScopeInfo j = _labelBlock; j != null; j = j.Parent) { if (j.Kind == LabelScopeKind.Filter) { throw Error.TryNotAllowedInFilter(); } } }
private void CheckRethrow() { // Rethrow is only valid inside a catch. for (LabelScopeInfo j = _labelBlock; j != null; j = j.Parent) { if (j.Kind == LabelScopeKind.Catch) { return; } else if (j.Kind == LabelScopeKind.Finally) { // Rethrow from inside finally is not verifiable break; } } throw Error.RethrowRequiresCatch(); }
// Returns true if the label was successfully defined // or false if the label is now ambiguous internal void Define(LabelScopeInfo block) { // Prevent the label from being shadowed, which enforces cleaner // trees. Also we depend on this for simplicity (keeping only one // active IL Label per LabelInfo) for (LabelScopeInfo j = block; j != null; j = j.Parent) { if (j.ContainsTarget(_node)) { throw Error.LabelTargetAlreadyDefined(_node.Name); } } _definitions.Add(block); block.AddLabelInfo(_node, this); // Once defined, validate all jumps if (_definitions.Count == 1) { foreach (var r in _references) { ValidateJump(r); } } else { // Was just redefined, if we had any across block jumps, they're // now invalid if (_acrossBlockJump) { throw Error.AmbiguousJump(_node.Name); } // For local jumps, we need a new IL label // This is okay because: // 1. no across block jumps have been made or will be made // 2. we don't allow the label to be shadowed _labelDefined = false; } }
private void PushLabelBlock(LabelScopeKind type) { _labelBlock = new LabelScopeInfo(_labelBlock, type); }
internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) { Parent = parent; Kind = kind; }
private void ValidateJump(LabelScopeInfo reference) { // Assume we can do a ret/branch _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; // look for a simple jump out for (LabelScopeInfo j = reference; j != null; j = j.Parent) { if (_definitions.Contains(j)) { // found it, jump is valid! return; } if (j.Kind == LabelScopeKind.Finally || j.Kind == LabelScopeKind.Filter) { break; } if (j.Kind == LabelScopeKind.Try || j.Kind == LabelScopeKind.Catch) { _opCode = OpCodes.Leave; } } _acrossBlockJump = true; if (_node != null && _node.Type != typeof(void)) { throw Error.NonLocalJumpWithValue(_node.Name); } if (_definitions.Count > 1) { throw Error.AmbiguousJump(_node.Name); } // We didn't find an outward jump. Look for a jump across blocks LabelScopeInfo def = _definitions.First(); LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent); // Assume we can do a ret/branch _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br; // Validate that we aren't jumping across a finally for (LabelScopeInfo j = reference; j != common; j = j.Parent) { if (j.Kind == LabelScopeKind.Finally) { throw Error.ControlCannotLeaveFinally(); } if (j.Kind == LabelScopeKind.Filter) { throw Error.ControlCannotLeaveFilterTest(); } if (j.Kind == LabelScopeKind.Try || j.Kind == LabelScopeKind.Catch) { _opCode = OpCodes.Leave; } } // Valdiate that we aren't jumping into a catch or an expression for (LabelScopeInfo j = def; j != common; j = j.Parent) { if (!j.CanJumpInto) { if (j.Kind == LabelScopeKind.Expression) { throw Error.ControlCannotEnterExpression(); } else { throw Error.ControlCannotEnterTry(); } } } }
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; } }