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;
117 if (rc.HasSet (ResolveContext.Options.QueryClauseScope))
118 flags |= ResolveContext.Options.QueryClauseScope;
121 public ExceptionStatement CurrentTryBlock { get; set; }
123 public TryCatch CurrentTryCatch { get; set; }
125 public LoopStatement EnclosingLoop { get; set; }
127 public LoopStatement EnclosingLoopOrSwitch { get; set; }
129 public Switch Switch { get; set; }
131 public TypeSpec ReturnType {
132 get { return return_type; }
137 // Expression resolving context
139 public class ResolveContext : IMemberContext
145 /// This flag tracks the `checked' state of the compilation,
146 /// it controls whether we should generate code that does overflow
147 /// checking, or if we generate code that ignores overflows.
149 /// The default setting comes from the command line option to generate
150 /// checked or unchecked code plus any source code changes using the
151 /// checked/unchecked statements or expressions. Contrast this with
152 /// the ConstantCheckState flag.
154 CheckedScope = 1 << 0,
157 /// The constant check state is always set to `true' and cant be changed
158 /// from the command line. The source code can change this setting with
159 /// the `checked' and `unchecked' statements and expressions.
161 ConstantCheckState = 1 << 1,
163 AllCheckStateFlags = CheckedScope | ConstantCheckState,
166 // unsafe { ... } scope
168 UnsafeScope = 1 << 2,
170 FinallyScope = 1 << 4,
171 FieldInitializerScope = 1 << 5,
172 CompoundAssignmentScope = 1 << 6,
173 FixedInitializerScope = 1 << 7,
174 BaseInitializer = 1 << 8,
177 // Inside an enum definition, we do not resolve enumeration values
178 // to their enumerations, but rather to the underlying type/value
179 // This is so EnumVal + EnumValB can be evaluated.
181 // There is no "E operator + (E x, E y)", so during an enum evaluation
182 // we relax the rules
186 ConstantScope = 1 << 10,
188 ConstructorScope = 1 << 11,
190 UsingInitializerScope = 1 << 12,
196 TryWithCatchScope = 1 << 15,
198 DontSetConditionalAccessReceiver = 1 << 16,
200 NameOfScope = 1 << 17,
202 QueryClauseScope = 1 << 18,
205 /// Indicates the current context is in probing mode, no errors are reported.
207 ProbingMode = 1 << 22,
210 // Return and ContextualReturn statements will set the ReturnType
211 // value based on the expression types of each return statement
212 // instead of the method return type which is initially null.
214 InferReturnType = 1 << 23,
216 OmitDebuggingInfo = 1 << 24,
218 ExpressionTreeConversion = 1 << 25,
220 InvokeSpecialName = 1 << 26
223 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
224 // it's public so that we can use a struct at the callsite
225 public struct FlagsHandle : IDisposable
227 readonly ResolveContext ec;
228 readonly Options invmask, oldval;
230 public FlagsHandle (ResolveContext ec, Options flagsToSet)
231 : this (ec, flagsToSet, flagsToSet)
235 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
239 oldval = ec.flags & mask;
240 ec.flags = (ec.flags & invmask) | (val & mask);
242 // if ((mask & Options.ProbingMode) != 0)
243 // ec.Report.DisableReporting ();
246 public void Dispose ()
248 // if ((invmask & Options.ProbingMode) == 0)
249 // ec.Report.EnableReporting ();
251 ec.flags = (ec.flags & invmask) | oldval;
255 protected Options flags;
258 // Whether we are inside an anonymous method.
260 public AnonymousExpression CurrentAnonymousMethod;
263 // Holds a varible used during collection or object initialization.
265 public Expression CurrentInitializerVariable;
267 public Block CurrentBlock;
269 public readonly IMemberContext MemberContext;
271 public ResolveContext (IMemberContext mc)
274 throw new ArgumentNullException ();
279 // The default setting comes from the command line option
281 if (mc.Module.Compiler.Settings.Checked)
282 flags |= Options.CheckedScope;
285 // The constant check state is always set to true
287 flags |= Options.ConstantCheckState;
290 public ResolveContext (IMemberContext mc, Options options)
298 public BuiltinTypes BuiltinTypes {
300 return MemberContext.Module.Compiler.BuiltinTypes;
304 public virtual ExplicitBlock ConstructorBlock {
306 return CurrentBlock.Explicit;
311 // The current iterator
313 public Iterator CurrentIterator {
314 get { return CurrentAnonymousMethod as Iterator; }
317 public TypeSpec CurrentType {
318 get { return MemberContext.CurrentType; }
321 public TypeParameters CurrentTypeParameters {
322 get { return MemberContext.CurrentTypeParameters; }
325 public MemberCore CurrentMemberDefinition {
326 get { return MemberContext.CurrentMemberDefinition; }
329 public bool ConstantCheckState {
330 get { return (flags & Options.ConstantCheckState) != 0; }
333 public bool IsInProbingMode {
335 return (flags & Options.ProbingMode) != 0;
339 public bool IsObsolete {
341 // Disables obsolete checks when probing is on
342 return MemberContext.IsObsolete;
346 public bool IsStatic {
348 return MemberContext.IsStatic;
352 public bool IsUnsafe {
354 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
358 public bool IsRuntimeBinder {
360 return Module.Compiler.IsRuntimeBinder;
364 public bool IsVariableCapturingRequired {
366 return !IsInProbingMode;
370 public ModuleContainer Module {
372 return MemberContext.Module;
376 public Report Report {
378 return Module.Compiler.Report;
384 public bool MustCaptureVariable (INamedBlockVariable local)
386 if (CurrentAnonymousMethod == null)
390 // Capture only if this or any of child blocks contain yield
391 // or it's a parameter
393 if (CurrentAnonymousMethod.IsIterator)
394 return local.IsParameter || local.Block.Explicit.HasYield;
397 // Capture only if this or any of child blocks contain await
398 // or it's a parameter or we need to access variable from
399 // different parameter block
401 if (CurrentAnonymousMethod is AsyncInitializer)
402 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
403 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
405 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
408 public bool HasSet (Options options)
410 return (this.flags & options) == options;
413 public bool HasAny (Options options)
415 return (this.flags & options) != 0;
419 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
420 public FlagsHandle Set (Options options)
422 return new FlagsHandle (this, options);
425 public FlagsHandle With (Options options, bool enable)
427 return new FlagsHandle (this, options, enable ? options : 0);
430 #region IMemberContext Members
432 public string GetSignatureForError ()
434 return MemberContext.GetSignatureForError ();
437 public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
439 return MemberContext.LookupExtensionMethod (name, arity);
442 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
444 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
447 public FullNamedExpression LookupNamespaceAlias (string name)
449 return MemberContext.LookupNamespaceAlias (name);
455 public class FlowAnalysisContext
457 readonly CompilerContext ctx;
459 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
462 this.ParametersBlock = parametersBlock;
464 DefiniteAssignment = definiteAssignmentLength == 0 ?
465 DefiniteAssignmentBitSet.Empty :
466 new DefiniteAssignmentBitSet (definiteAssignmentLength);
469 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
471 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
473 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
475 Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
477 public ParametersBlock ParametersBlock { get; set; }
479 public Report Report {
485 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
487 public TryFinally TryFinally { get; set; }
489 public bool UnreachableReported { get; set; }
491 public bool AddReachedLabel (Statement label)
493 List<DefiniteAssignmentBitSet> das;
494 if (LabelStack == null) {
495 LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
498 LabelStack.TryGetValue (label, out das);
502 das = new List<DefiniteAssignmentBitSet> ();
503 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
504 LabelStack.Add (label, das);
508 foreach (var existing in das) {
509 if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
513 if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
514 das.Add (DefiniteAssignment);
516 das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
521 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
523 return BranchDefiniteAssignment (DefiniteAssignment);
526 public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
528 if (da != DefiniteAssignmentBitSet.Empty) {
529 DefiniteAssignment = new DefiniteAssignmentBitSet (da);
535 public Dictionary<Statement, List<DefiniteAssignmentBitSet>> CopyLabelStack ()
537 if (LabelStack == null)
540 var dest = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
541 foreach (var entry in LabelStack) {
542 dest.Add (entry.Key, new List<DefiniteAssignmentBitSet> (entry.Value));
548 public bool IsDefinitelyAssigned (VariableInfo variable)
550 return variable.IsAssigned (DefiniteAssignment);
553 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
555 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
558 public void SetLabelStack (Dictionary<Statement, List<DefiniteAssignmentBitSet>> labelStack)
560 LabelStack = labelStack;
563 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
565 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
568 public void SetStructFieldAssigned (VariableInfo variable, string name)
570 variable.SetStructFieldAssigned (DefiniteAssignment, name);
576 // This class is used during the Statement.Clone operation
577 // to remap objects that have been cloned.
579 // Since blocks are cloned by Block.Clone, we need a way for
580 // expressions that must reference the block to be cloned
581 // pointing to the new cloned block.
583 public class CloneContext
585 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
587 public void AddBlockMap (Block from, Block to)
589 block_map.Add (from, to);
592 public Block LookupBlock (Block from)
595 if (!block_map.TryGetValue (from, out result)) {
596 result = (Block) from.Clone (this);
603 /// Remaps block to cloned copy if one exists.
605 public Block RemapBlockCopy (Block from)
608 if (!block_map.TryGetValue (from, out mapped_to))
616 // Main compiler context
618 public class CompilerContext
620 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
622 readonly Report report;
623 readonly BuiltinTypes builtin_types;
624 readonly CompilerSettings settings;
626 Dictionary<string, SourceFile> all_source_files;
628 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
630 this.settings = settings;
631 this.report = new Report (this, reportPrinter);
632 this.builtin_types = new BuiltinTypes ();
633 this.TimeReporter = DisabledTimeReporter;
638 public BuiltinTypes BuiltinTypes {
640 return builtin_types;
644 // Used for special handling of runtime dynamic context mostly
645 // by error reporting but also by member accessibility checks
646 public bool IsRuntimeBinder {
650 public Report Report {
656 public CompilerSettings Settings {
662 public List<SourceFile> SourceFiles {
664 return settings.SourceFiles;
668 internal TimeReporter TimeReporter {
675 // This is used when we encounter a #line preprocessing directive during parsing
676 // to register additional source file names
678 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
680 if (all_source_files == null) {
681 all_source_files = new Dictionary<string, SourceFile> ();
682 foreach (var source in SourceFiles)
683 all_source_files[source.OriginalFullPathName] = source;
687 if (!Path.IsPathRooted (name)) {
688 var loc = comp_unit.SourceFile;
689 string root = Path.GetDirectoryName (loc.OriginalFullPathName);
690 path = Path.GetFullPath (Path.Combine (root, name));
691 var dir = Path.GetDirectoryName (loc.Name);
692 if (!string.IsNullOrEmpty (dir))
693 name = Path.Combine (dir, name);
698 if (all_source_files.TryGetValue (path, out retval))
701 retval = new SourceFile (name, path, all_source_files.Count + 1);
702 Location.AddFile (retval);
703 all_source_files.Add (path, retval);
709 // Generic code emitter context
711 public class BuilderContext
717 /// This flag tracks the `checked' state of the compilation,
718 /// it controls whether we should generate code that does overflow
719 /// checking, or if we generate code that ignores overflows.
721 /// The default setting comes from the command line option to generate
722 /// checked or unchecked code plus any source code changes using the
723 /// checked/unchecked statements or expressions. Contrast this with
724 /// the ConstantCheckState flag.
726 CheckedScope = 1 << 0,
728 AccurateDebugInfo = 1 << 1,
730 OmitDebugInfo = 1 << 2,
732 ConstructorScope = 1 << 3,
737 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
738 // it's public so that we can use a struct at the callsite
739 public struct FlagsHandle : IDisposable
741 readonly BuilderContext ec;
742 readonly Options invmask, oldval;
744 public FlagsHandle (BuilderContext ec, Options flagsToSet)
745 : this (ec, flagsToSet, flagsToSet)
749 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
753 oldval = ec.flags & mask;
754 ec.flags = (ec.flags & invmask) | (val & mask);
757 public void Dispose ()
759 ec.flags = (ec.flags & invmask) | oldval;
763 protected Options flags;
765 public bool HasSet (Options options)
767 return (this.flags & options) == options;
770 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
771 public FlagsHandle With (Options options, bool enable)
773 return new FlagsHandle (this, options, enable ? options : 0);
778 // Parser session objects. We could recreate all these objects for each parser
779 // instance but the best parser performance the session object can be reused
781 public class ParserSession
785 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
786 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
787 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
788 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
789 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
791 public LocationsBag LocationsBag { get; set; }
792 public bool UseJayGlobalArrays { get; set; }
793 public LocatedToken[] LocatedTokens { get; set; }
795 public MD5 GetChecksumAlgorithm ()
797 return md5 ?? (md5 = MD5.Create ());