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;
20 public enum LookupMode
24 IgnoreAccessibility = 2
28 // Implemented by elements which can act as independent contexts
29 // during resolve phase. Used mostly for lookups.
31 public interface IMemberContext : IModuleContext
34 // A scope type context, it can be inflated for generic types
36 TypeSpec CurrentType { get; }
39 // A scope type parameters either VAR or MVAR
41 TypeParameters CurrentTypeParameters { get; }
44 // A member definition of the context. For partial types definition use
45 // CurrentTypeDefinition.PartialContainer otherwise the context is local
47 // TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
49 MemberCore CurrentMemberDefinition { get; }
51 bool IsObsolete { get; }
52 bool IsUnsafe { get; }
53 bool IsStatic { get; }
55 string GetSignatureForError ();
57 ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity);
58 FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
59 FullNamedExpression LookupNamespaceAlias (string name);
62 public interface IModuleContext
64 ModuleContainer Module { get; }
68 // Block or statement resolving context
70 public class BlockContext : ResolveContext
72 readonly TypeSpec return_type;
75 // Tracks the last offset used by VariableInfo
77 public int AssignmentInfoOffset;
79 public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
82 if (returnType == null)
83 throw new ArgumentNullException ("returnType");
85 this.return_type = returnType;
87 // TODO: check for null value
91 public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
92 : this (rc.MemberContext, block, returnType)
95 flags |= ResolveContext.Options.UnsafeScope;
97 if (rc.HasSet (ResolveContext.Options.CheckedScope))
98 flags |= ResolveContext.Options.CheckedScope;
100 if (!rc.ConstantCheckState)
101 flags &= ~Options.ConstantCheckState;
103 if (rc.IsInProbingMode)
104 flags |= ResolveContext.Options.ProbingMode;
106 if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
107 flags |= ResolveContext.Options.FieldInitializerScope;
109 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
110 flags |= ResolveContext.Options.ExpressionTreeConversion;
112 if (rc.HasSet (ResolveContext.Options.BaseInitializer))
113 flags |= ResolveContext.Options.BaseInitializer;
116 public ExceptionStatement CurrentTryBlock { get; set; }
118 public LoopStatement EnclosingLoop { get; set; }
120 public LoopStatement EnclosingLoopOrSwitch { get; set; }
122 public Switch Switch { get; set; }
124 public TypeSpec ReturnType {
125 get { return return_type; }
130 // Expression resolving context
132 public class ResolveContext : IMemberContext
138 /// This flag tracks the `checked' state of the compilation,
139 /// it controls whether we should generate code that does overflow
140 /// checking, or if we generate code that ignores overflows.
142 /// The default setting comes from the command line option to generate
143 /// checked or unchecked code plus any source code changes using the
144 /// checked/unchecked statements or expressions. Contrast this with
145 /// the ConstantCheckState flag.
147 CheckedScope = 1 << 0,
150 /// The constant check state is always set to `true' and cant be changed
151 /// from the command line. The source code can change this setting with
152 /// the `checked' and `unchecked' statements and expressions.
154 ConstantCheckState = 1 << 1,
156 AllCheckStateFlags = CheckedScope | ConstantCheckState,
159 // unsafe { ... } scope
161 UnsafeScope = 1 << 2,
163 FinallyScope = 1 << 4,
164 FieldInitializerScope = 1 << 5,
165 CompoundAssignmentScope = 1 << 6,
166 FixedInitializerScope = 1 << 7,
167 BaseInitializer = 1 << 8,
170 // Inside an enum definition, we do not resolve enumeration values
171 // to their enumerations, but rather to the underlying type/value
172 // This is so EnumVal + EnumValB can be evaluated.
174 // There is no "E operator + (E x, E y)", so during an enum evaluation
175 // we relax the rules
179 ConstantScope = 1 << 10,
181 ConstructorScope = 1 << 11,
183 UsingInitializerScope = 1 << 12,
189 TryWithCatchScope = 1 << 15,
192 /// Indicates the current context is in probing mode, no errors are reported.
194 ProbingMode = 1 << 22,
197 // Return and ContextualReturn statements will set the ReturnType
198 // value based on the expression types of each return statement
199 // instead of the method return type which is initially null.
201 InferReturnType = 1 << 23,
203 OmitDebuggingInfo = 1 << 24,
205 ExpressionTreeConversion = 1 << 25,
207 InvokeSpecialName = 1 << 26
210 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
211 // it's public so that we can use a struct at the callsite
212 public struct FlagsHandle : IDisposable
214 readonly ResolveContext ec;
215 readonly Options invmask, oldval;
217 public FlagsHandle (ResolveContext ec, Options flagsToSet)
218 : this (ec, flagsToSet, flagsToSet)
222 internal FlagsHandle (ResolveContext ec, Options mask, Options val)
226 oldval = ec.flags & mask;
227 ec.flags = (ec.flags & invmask) | (val & mask);
229 // if ((mask & Options.ProbingMode) != 0)
230 // ec.Report.DisableReporting ();
233 public void Dispose ()
235 // if ((invmask & Options.ProbingMode) == 0)
236 // ec.Report.EnableReporting ();
238 ec.flags = (ec.flags & invmask) | oldval;
242 protected Options flags;
245 // Whether we are inside an anonymous method.
247 public AnonymousExpression CurrentAnonymousMethod;
250 // Holds a varible used during collection or object initialization.
252 public Expression CurrentInitializerVariable;
254 public Block CurrentBlock;
256 public readonly IMemberContext MemberContext;
258 public ResolveContext (IMemberContext mc)
261 throw new ArgumentNullException ();
266 // The default setting comes from the command line option
268 if (mc.Module.Compiler.Settings.Checked)
269 flags |= Options.CheckedScope;
272 // The constant check state is always set to true
274 flags |= Options.ConstantCheckState;
277 public ResolveContext (IMemberContext mc, Options options)
285 public BuiltinTypes BuiltinTypes {
287 return MemberContext.Module.Compiler.BuiltinTypes;
291 public virtual ExplicitBlock ConstructorBlock {
293 return CurrentBlock.Explicit;
298 // The current iterator
300 public Iterator CurrentIterator {
301 get { return CurrentAnonymousMethod as Iterator; }
304 public TypeSpec CurrentType {
305 get { return MemberContext.CurrentType; }
308 public TypeParameters CurrentTypeParameters {
309 get { return MemberContext.CurrentTypeParameters; }
312 public MemberCore CurrentMemberDefinition {
313 get { return MemberContext.CurrentMemberDefinition; }
316 public bool ConstantCheckState {
317 get { return (flags & Options.ConstantCheckState) != 0; }
320 public bool IsInProbingMode {
322 return (flags & Options.ProbingMode) != 0;
326 public bool IsObsolete {
328 // Disables obsolete checks when probing is on
329 return MemberContext.IsObsolete;
333 public bool IsStatic {
335 return MemberContext.IsStatic;
339 public bool IsUnsafe {
341 return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
345 public bool IsRuntimeBinder {
347 return Module.Compiler.IsRuntimeBinder;
351 public bool IsVariableCapturingRequired {
353 return !IsInProbingMode;
357 public ModuleContainer Module {
359 return MemberContext.Module;
363 public Report Report {
365 return Module.Compiler.Report;
371 public bool MustCaptureVariable (INamedBlockVariable local)
373 if (CurrentAnonymousMethod == null)
377 // Capture only if this or any of child blocks contain yield
378 // or it's a parameter
380 if (CurrentAnonymousMethod.IsIterator)
381 return local.IsParameter || local.Block.Explicit.HasYield;
384 // Capture only if this or any of child blocks contain await
385 // or it's a parameter or we need to access variable from
386 // different parameter block
388 if (CurrentAnonymousMethod is AsyncInitializer)
389 return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
390 local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
392 return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
395 public bool HasSet (Options options)
397 return (this.flags & options) == options;
400 public bool HasAny (Options options)
402 return (this.flags & options) != 0;
406 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
407 public FlagsHandle Set (Options options)
409 return new FlagsHandle (this, options);
412 public FlagsHandle With (Options options, bool enable)
414 return new FlagsHandle (this, options, enable ? options : 0);
417 #region IMemberContext Members
419 public string GetSignatureForError ()
421 return MemberContext.GetSignatureForError ();
424 public ExtensionMethodCandidates LookupExtensionMethod (TypeSpec extensionType, string name, int arity)
426 return MemberContext.LookupExtensionMethod (extensionType, name, arity);
429 public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
431 return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
434 public FullNamedExpression LookupNamespaceAlias (string name)
436 return MemberContext.LookupNamespaceAlias (name);
442 public class FlowAnalysisContext
444 readonly CompilerContext ctx;
446 public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
449 this.ParametersBlock = parametersBlock;
451 DefiniteAssignment = definiteAssignmentLength == 0 ?
452 DefiniteAssignmentBitSet.Empty :
453 new DefiniteAssignmentBitSet (definiteAssignmentLength);
456 public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
458 public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
460 public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
462 public List<LabeledStatement> LabelStack { get; set; }
464 public ParametersBlock ParametersBlock { get; set; }
466 public Report Report {
472 public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
474 public TryFinally TryFinally { get; set; }
476 public bool UnreachableReported { get; set; }
478 public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
480 var dat = DefiniteAssignment;
481 if (dat != DefiniteAssignmentBitSet.Empty)
482 DefiniteAssignment = new DefiniteAssignmentBitSet (dat);
486 public bool IsDefinitelyAssigned (VariableInfo variable)
488 return variable.IsAssigned (DefiniteAssignment);
491 public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
493 return variable.IsStructFieldAssigned (DefiniteAssignment, name);
496 public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
498 variable.SetAssigned (DefiniteAssignment, generatedAssignment);
501 public void SetStructFieldAssigned (VariableInfo variable, string name)
503 variable.SetStructFieldAssigned (DefiniteAssignment, name);
509 // This class is used during the Statement.Clone operation
510 // to remap objects that have been cloned.
512 // Since blocks are cloned by Block.Clone, we need a way for
513 // expressions that must reference the block to be cloned
514 // pointing to the new cloned block.
516 public class CloneContext
518 Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
520 public void AddBlockMap (Block from, Block to)
522 block_map.Add (from, to);
525 public Block LookupBlock (Block from)
528 if (!block_map.TryGetValue (from, out result)) {
529 result = (Block) from.Clone (this);
536 /// Remaps block to cloned copy if one exists.
538 public Block RemapBlockCopy (Block from)
541 if (!block_map.TryGetValue (from, out mapped_to))
549 // Main compiler context
551 public class CompilerContext
553 static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
555 readonly Report report;
556 readonly BuiltinTypes builtin_types;
557 readonly CompilerSettings settings;
559 Dictionary<string, SourceFile> all_source_files;
561 public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
563 this.settings = settings;
564 this.report = new Report (this, reportPrinter);
565 this.builtin_types = new BuiltinTypes ();
566 this.TimeReporter = DisabledTimeReporter;
571 public BuiltinTypes BuiltinTypes {
573 return builtin_types;
577 // Used for special handling of runtime dynamic context mostly
578 // by error reporting but also by member accessibility checks
579 public bool IsRuntimeBinder {
583 public Report Report {
589 public CompilerSettings Settings {
595 public List<SourceFile> SourceFiles {
597 return settings.SourceFiles;
601 internal TimeReporter TimeReporter {
608 // This is used when we encounter a #line preprocessing directive during parsing
609 // to register additional source file names
611 public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
613 if (all_source_files == null) {
614 all_source_files = new Dictionary<string, SourceFile> ();
615 foreach (var source in SourceFiles)
616 all_source_files[source.FullPathName] = source;
620 if (!Path.IsPathRooted (name)) {
621 var loc = comp_unit.SourceFile;
622 string root = Path.GetDirectoryName (loc.FullPathName);
623 path = Path.GetFullPath (Path.Combine (root, name));
624 var dir = Path.GetDirectoryName (loc.Name);
625 if (!string.IsNullOrEmpty (dir))
626 name = Path.Combine (dir, name);
631 if (all_source_files.TryGetValue (path, out retval))
634 retval = new SourceFile (name, path, all_source_files.Count + 1);
635 Location.AddFile (retval);
636 all_source_files.Add (path, retval);
642 // Generic code emitter context
644 public class BuilderContext
650 /// This flag tracks the `checked' state of the compilation,
651 /// it controls whether we should generate code that does overflow
652 /// checking, or if we generate code that ignores overflows.
654 /// The default setting comes from the command line option to generate
655 /// checked or unchecked code plus any source code changes using the
656 /// checked/unchecked statements or expressions. Contrast this with
657 /// the ConstantCheckState flag.
659 CheckedScope = 1 << 0,
661 AccurateDebugInfo = 1 << 1,
663 OmitDebugInfo = 1 << 2,
665 ConstructorScope = 1 << 3,
670 // utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
671 // it's public so that we can use a struct at the callsite
672 public struct FlagsHandle : IDisposable
674 readonly BuilderContext ec;
675 readonly Options invmask, oldval;
677 public FlagsHandle (BuilderContext ec, Options flagsToSet)
678 : this (ec, flagsToSet, flagsToSet)
682 internal FlagsHandle (BuilderContext ec, Options mask, Options val)
686 oldval = ec.flags & mask;
687 ec.flags = (ec.flags & invmask) | (val & mask);
690 public void Dispose ()
692 ec.flags = (ec.flags & invmask) | oldval;
696 protected Options flags;
698 public bool HasSet (Options options)
700 return (this.flags & options) == options;
703 // Temporarily set all the given flags to the given value. Should be used in an 'using' statement
704 public FlagsHandle With (Options options, bool enable)
706 return new FlagsHandle (this, options, enable ? options : 0);
711 // Parser session objects. We could recreate all these objects for each parser
712 // instance but the best parser performance the session object can be reused
714 public class ParserSession
718 public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
719 public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
720 public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
721 public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
722 public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
724 public LocationsBag LocationsBag { get; set; }
725 public bool UseJayGlobalArrays { get; set; }
726 public LocatedToken[] LocatedTokens { get; set; }
728 public MD5 GetChecksumAlgorithm ()
730 return md5 ?? (md5 = MD5.Create ());