2 // context.cs: Various compiler contexts.
5 // Marek Safar (marek.safar@gmail.com)
6 // Miguel de Icaza (miguel@ximian.com)
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2004-2009 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
14 using System.Collections.Generic;
16 using System.Security.Cryptography;
17 using System.Diagnostics;
21 public enum LookupMode
25 IgnoreAccessibility = 2,
26 IgnoreStaticUsing = 1 << 10
30 // Implemented by elements which can act as independent contexts
31 // during resolve phase. Used mostly for lookups.
33 public interface IMemberContext : IModuleContext
36 // A scope type context, it can be inflated for generic types
38 TypeSpec CurrentType { get; }
41 // A scope type parameters either VAR or MVAR
43 TypeParameters CurrentTypeParameters { get; }
46 // A member definition of the context. For partial types definition use
47 // CurrentTypeDefinition.PartialContainer otherwise the context is local
49 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
51 MemberCore CurrentMemberDefinition { get; }
53 bool IsObsolete { get; }
54 bool IsUnsafe { get; }
55 bool IsStatic { get; }
57 string GetSignatureForError ();
59 ExtensionMethodCandidates LookupExtensionMethod (string name, int arity);
60 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
61 FullNamedExpression LookupNamespaceAlias (string name);
64 public interface IModuleContext
66 ModuleContainer Module { get; }
70 // Block or statement resolving context
72 public class BlockContext : ResolveContext
74 readonly TypeSpec return_type;
77 // Tracks the last offset used by VariableInfo
79 public int AssignmentInfoOffset;
81 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
84 if (returnType == null)
85 throw new ArgumentNullException ("returnType");
87 this.return_type = returnType;
89 // TODO: check for null value
93 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
94 : this (rc.MemberContext, block, returnType)
97 flags |= ResolveContext.Options.UnsafeScope;
99 if (rc.HasSet (ResolveContext.Options.CheckedScope))
100 flags |= ResolveContext.Options.CheckedScope;
102 if (!rc.ConstantCheckState)
103 flags &= ~Options.ConstantCheckState;
105 if (rc.IsInProbingMode)
106 flags |= ResolveContext.Options.ProbingMode;
108 if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
109 flags |= ResolveContext.Options.FieldInitializerScope;
111 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
112 flags |= ResolveContext.Options.ExpressionTreeConversion;
114 if (rc.HasSet (ResolveContext.Options.BaseInitializer))
115 flags |= ResolveContext.Options.BaseInitializer;
118 public ExceptionStatement CurrentTryBlock { get; set; }
120 public TryCatch CurrentTryCatch { get; set; }
122 public LoopStatement EnclosingLoop { get; set; }
124 public LoopStatement EnclosingLoopOrSwitch { get; set; }
126 public Switch Switch { get; set; }
128 public TypeSpec ReturnType {
129 get { return return_type; }
134 // Expression resolving context
136 public class ResolveContext : IMemberContext
142 /// This flag tracks the `checked' state of the compilation,
143 /// it controls whether we should generate code that does overflow
144 /// checking, or if we generate code that ignores overflows.
146 /// The default setting comes from the command line option to generate
147 /// checked or unchecked code plus any source code changes using the
148 /// checked/unchecked statements or expressions. Contrast this with
149 /// the ConstantCheckState flag.
151 CheckedScope = 1 << 0,
154 /// The constant check state is always set to `true' and cant be changed
155 /// from the command line. The source code can change this setting with
156 /// the `checked' and `unchecked' statements and expressions.
158 ConstantCheckState = 1 << 1,
160 AllCheckStateFlags = CheckedScope | ConstantCheckState,
163 // unsafe { ... } scope
165 UnsafeScope = 1 << 2,
167 FinallyScope = 1 << 4,
168 FieldInitializerScope = 1 << 5,
169 CompoundAssignmentScope = 1 << 6,
170 FixedInitializerScope = 1 << 7,
171 BaseInitializer = 1 << 8,
174 // Inside an enum definition, we do not resolve enumeration values
175 // to their enumerations, but rather to the underlying type/value
176 // This is so EnumVal + EnumValB can be evaluated.
178 // There is no "E operator + (E x, E y)", so during an enum evaluation
179 // we relax the rules
183 ConstantScope = 1 << 10,
185 ConstructorScope = 1 << 11,
187 UsingInitializerScope = 1 << 12,
193 TryWithCatchScope = 1 << 15,
195 DontSetConditionalAccessReceiver = 1 << 16,
197 NameOfScope = 1 << 17,
200 /// Indicates the current context is in probing mode, no errors are reported.
202 ProbingMode = 1 << 22,
205 // Return and ContextualReturn statements will set the ReturnType
206 // value based on the expression types of each return statement
207 // instead of the method return type which is initially null.
209 InferReturnType = 1 << 23,
211 OmitDebuggingInfo = 1 << 24,
213 ExpressionTreeConversion = 1 << 25,
215 InvokeSpecialName = 1 << 26
218 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
219 // it's public so that we can use a struct at the callsite
220 public struct FlagsHandle : IDisposable
222 readonly ResolveContext ec;
223 readonly Options invmask, oldval;
225 public FlagsHandle (ResolveContext ec, Options flagsToSet)
226 : this (ec, flagsToSet, flagsToSet)
230 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
234 oldval = ec.flags & mask;
235 ec.flags = (ec.flags & invmask) | (val & mask);
237 // if ((mask & Options.ProbingMode) != 0)
238 // ec.Report.DisableReporting ();
241 public void Dispose ()
243 // if ((invmask & Options.ProbingMode) == 0)
244 // ec.Report.EnableReporting ();
246 ec.flags = (ec.flags & invmask) | oldval;
250 protected Options flags;
253 // Whether we are inside an anonymous method.
255 public AnonymousExpression CurrentAnonymousMethod;
258 // Holds a varible used during collection or object initialization.
260 public Expression CurrentInitializerVariable;
262 public Block CurrentBlock;
264 public readonly IMemberContext MemberContext;
266 public ResolveContext (IMemberContext mc)
269 throw new ArgumentNullException ();
274 // The default setting comes from the command line option
276 if (mc.Module.Compiler.Settings.Checked)
277 flags |= Options.CheckedScope;
280 // The constant check state is always set to true
282 flags |= Options.ConstantCheckState;
285 public ResolveContext (IMemberContext mc, Options options)
293 public BuiltinTypes BuiltinTypes {
295 return MemberContext.Module.Compiler.BuiltinTypes;
299 public virtual ExplicitBlock ConstructorBlock {
301 return CurrentBlock.Explicit;
306 // The current iterator
308 public Iterator CurrentIterator {
309 get { return CurrentAnonymousMethod as Iterator; }
312 public TypeSpec CurrentType {
313 get { return MemberContext.CurrentType; }
316 public TypeParameters CurrentTypeParameters {
317 get { return MemberContext.CurrentTypeParameters; }
320 public MemberCore CurrentMemberDefinition {
321 get { return MemberContext.CurrentMemberDefinition; }
324 public bool ConstantCheckState {
325 get { return (flags & Options.ConstantCheckState) != 0; }
328 public bool IsInProbingMode {
330 return (flags & Options.ProbingMode) != 0;
334 public bool IsObsolete {
336 // Disables obsolete checks when probing is on
337 return MemberContext.IsObsolete;
341 public bool IsStatic {
343 return MemberContext.IsStatic;
347 public bool IsUnsafe {
349 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
353 public bool IsRuntimeBinder {
355 return Module.Compiler.IsRuntimeBinder;
359 public bool IsVariableCapturingRequired {
361 return !IsInProbingMode;
365 public ModuleContainer Module {
367 return MemberContext.Module;
371 public Report Report {
373 return Module.Compiler.Report;
379 public bool MustCaptureVariable (INamedBlockVariable local)
381 if (CurrentAnonymousMethod == null)
385 // Capture only if this or any of child blocks contain yield
386 // or it's a parameter
388 if (CurrentAnonymousMethod.IsIterator)
389 return local.IsParameter || local.Block.Explicit.HasYield;
392 // Capture only if this or any of child blocks contain await
393 // or it's a parameter or we need to access variable from
394 // different parameter block
396 if (CurrentAnonymousMethod is AsyncInitializer)
397 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
398 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
400 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
403 public bool HasSet (Options options)
405 return (this.flags & options) == options;
408 public bool HasAny (Options options)
410 return (this.flags & options) != 0;
414 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
415 public FlagsHandle Set (Options options)
417 return new FlagsHandle (this, options);
420 public FlagsHandle With (Options options, bool enable)
422 return new FlagsHandle (this, options, enable ? options : 0);
425 #region IMemberContext Members
427 public string GetSignatureForError ()
429 return MemberContext.GetSignatureForError ();
432 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
434 return MemberContext.LookupExtensionMethod (name, arity);
437 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
439 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
442 public FullNamedExpression LookupNamespaceAlias (string name)
444 return MemberContext.LookupNamespaceAlias (name);
450 public class FlowAnalysisContext
452 readonly CompilerContext ctx;
454 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
457 this.ParametersBlock = parametersBlock;
459 DefiniteAssignment = definiteAssignmentLength == 0 ?
460 DefiniteAssignmentBitSet.Empty :
461 new DefiniteAssignmentBitSet (definiteAssignmentLength);
464 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
466 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
468 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
470 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
472 public ParametersBlock ParametersBlock { get; set; }
474 public Report Report {
480 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
482 public TryFinally TryFinally { get; set; }
484 public bool UnreachableReported { get; set; }
486 public bool AddReachedLabel (Statement label)
488 List<DefiniteAssignmentBitSet> das;
489 if (LabelStack == null) {
490 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
493 LabelStack.TryGetValue (label, out das);
497 das = new List<DefiniteAssignmentBitSet> ();
498 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
499 LabelStack.Add (label, das);
503 foreach (var existing in das) {
504 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
508 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
509 das.Add (DefiniteAssignment);
511 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
516 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
518 return BranchDefiniteAssignment (DefiniteAssignment);
521 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
523 if (da != DefiniteAssignmentBitSet.Empty) {
524 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
530 public Dictionary<Statement, List<DefiniteAssignmentBitSet>> CopyLabelStack ()
532 if (LabelStack == null)
535 var dest = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
536 foreach (var entry in LabelStack) {
537 dest.Add (entry.Key, new List<DefiniteAssignmentBitSet> (entry.Value));
543 public bool IsDefinitelyAssigned (VariableInfo variable)
545 return variable.IsAssigned (DefiniteAssignment);
548 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
550 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
553 public void SetLabelStack (Dictionary<Statement, List<DefiniteAssignmentBitSet>> labelStack)
555 LabelStack = labelStack;
558 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
560 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
563 public void SetStructFieldAssigned (VariableInfo variable, string name)
565 variable.SetStructFieldAssigned (DefiniteAssignment, name);
571 // This class is used during the Statement.Clone operation
572 // to remap objects that have been cloned.
574 // Since blocks are cloned by Block.Clone, we need a way for
575 // expressions that must reference the block to be cloned
576 // pointing to the new cloned block.
578 public class CloneContext
580 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
582 public void AddBlockMap (Block from, Block to)
584 block_map.Add (from, to);
587 public Block LookupBlock (Block from)
590 if (!block_map.TryGetValue (from, out result)) {
591 result = (Block) from.Clone (this);
598 /// Remaps block to cloned copy if one exists.
600 public Block RemapBlockCopy (Block from)
603 if (!block_map.TryGetValue (from, out mapped_to))
611 // Main compiler context
613 public class CompilerContext
615 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
617 readonly Report report;
618 readonly BuiltinTypes builtin_types;
619 readonly CompilerSettings settings;
621 Dictionary<string, SourceFile> all_source_files;
623 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
625 this.settings = settings;
626 this.report = new Report (this, reportPrinter);
627 this.builtin_types = new BuiltinTypes ();
628 this.TimeReporter = DisabledTimeReporter;
633 public BuiltinTypes BuiltinTypes {
635 return builtin_types;
639 // Used for special handling of runtime dynamic context mostly
640 // by error reporting but also by member accessibility checks
641 public bool IsRuntimeBinder {
645 public Report Report {
651 public CompilerSettings Settings {
657 public List<SourceFile> SourceFiles {
659 return settings.SourceFiles;
663 internal TimeReporter TimeReporter {
670 // This is used when we encounter a #line preprocessing directive during parsing
671 // to register additional source file names
673 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
675 if (all_source_files == null) {
676 all_source_files = new Dictionary<string, SourceFile> ();
677 foreach (var source in SourceFiles)
678 all_source_files[source.OriginalFullPathName] = source;
682 if (!Path.IsPathRooted (name)) {
683 var loc = comp_unit.SourceFile;
684 string root = Path.GetDirectoryName (loc.OriginalFullPathName);
685 path = Path.GetFullPath (Path.Combine (root, name));
686 var dir = Path.GetDirectoryName (loc.Name);
687 if (!string.IsNullOrEmpty (dir))
688 name = Path.Combine (dir, name);
693 if (all_source_files.TryGetValue (path, out retval))
696 retval = new SourceFile (name, path, all_source_files.Count + 1);
697 Location.AddFile (retval);
698 all_source_files.Add (path, retval);
704 // Generic code emitter context
706 public class BuilderContext
712 /// This flag tracks the `checked' state of the compilation,
713 /// it controls whether we should generate code that does overflow
714 /// checking, or if we generate code that ignores overflows.
716 /// The default setting comes from the command line option to generate
717 /// checked or unchecked code plus any source code changes using the
718 /// checked/unchecked statements or expressions. Contrast this with
719 /// the ConstantCheckState flag.
721 CheckedScope = 1 << 0,
723 AccurateDebugInfo = 1 << 1,
725 OmitDebugInfo = 1 << 2,
727 ConstructorScope = 1 << 3,
732 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
733 // it's public so that we can use a struct at the callsite
734 public struct FlagsHandle : IDisposable
736 readonly BuilderContext ec;
737 readonly Options invmask, oldval;
739 public FlagsHandle (BuilderContext ec, Options flagsToSet)
740 : this (ec, flagsToSet, flagsToSet)
744 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
748 oldval = ec.flags & mask;
749 ec.flags = (ec.flags & invmask) | (val & mask);
752 public void Dispose ()
754 ec.flags = (ec.flags & invmask) | oldval;
758 protected Options flags;
760 public bool HasSet (Options options)
762 return (this.flags & options) == options;
765 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
766 public FlagsHandle With (Options options, bool enable)
768 return new FlagsHandle (this, options, enable ? options : 0);
773 // Parser session objects. We could recreate all these objects for each parser
774 // instance but the best parser performance the session object can be reused
776 public class ParserSession
780 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
781 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
782 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
783 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
784 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
786 public LocationsBag LocationsBag { get; set; }
787 public bool UseJayGlobalArrays { get; set; }
788 public LocatedToken[] LocatedTokens { get; set; }
790 public MD5 GetChecksumAlgorithm ()
792 return md5 ?? (md5 = MD5.Create ());