Xamarin Public Jenkins bdd40f83c0 Imported Upstream version 4.2.1.60
Former-commit-id: 05052d1d7a3a94b0d9ee70461d62b6591e5ab5bc
2015-11-10 14:54:44 +00:00

773 lines
19 KiB
C#

//
// context.cs: Various compiler contexts.
//
// Author:
// Marek Safar (marek.safar@gmail.com)
// Miguel de Icaza (miguel@ximian.com)
//
// Copyright 2001, 2002, 2003 Ximian, Inc.
// Copyright 2004-2009 Novell, Inc.
// Copyright 2011 Xamarin Inc.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
namespace Mono.CSharp
{
public enum LookupMode
{
Normal = 0,
Probing = 1,
IgnoreAccessibility = 2
}
//
// Implemented by elements which can act as independent contexts
// during resolve phase. Used mostly for lookups.
//
public interface IMemberContext : IModuleContext
{
//
// A scope type context, it can be inflated for generic types
//
TypeSpec CurrentType { get; }
//
// A scope type parameters either VAR or MVAR
//
TypeParameters CurrentTypeParameters { get; }
//
// A member definition of the context. For partial types definition use
// CurrentTypeDefinition.PartialContainer otherwise the context is local
//
// TODO: Obsolete it in this context, dynamic context cannot guarantee sensible value
//
MemberCore CurrentMemberDefinition { get; }
bool IsObsolete { get; }
bool IsUnsafe { get; }
bool IsStatic { get; }
string GetSignatureForError ();
ExtensionMethodCandidates LookupExtensionMethod (string name, int arity);
FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc);
FullNamedExpression LookupNamespaceAlias (string name);
}
public interface IModuleContext
{
ModuleContainer Module { get; }
}
//
// Block or statement resolving context
//
public class BlockContext : ResolveContext
{
readonly TypeSpec return_type;
//
// Tracks the last offset used by VariableInfo
//
public int AssignmentInfoOffset;
public BlockContext (IMemberContext mc, ExplicitBlock block, TypeSpec returnType)
: base (mc)
{
if (returnType == null)
throw new ArgumentNullException ("returnType");
this.return_type = returnType;
// TODO: check for null value
CurrentBlock = block;
}
public BlockContext (ResolveContext rc, ExplicitBlock block, TypeSpec returnType)
: this (rc.MemberContext, block, returnType)
{
if (rc.IsUnsafe)
flags |= ResolveContext.Options.UnsafeScope;
if (rc.HasSet (ResolveContext.Options.CheckedScope))
flags |= ResolveContext.Options.CheckedScope;
if (!rc.ConstantCheckState)
flags &= ~Options.ConstantCheckState;
if (rc.IsInProbingMode)
flags |= ResolveContext.Options.ProbingMode;
if (rc.HasSet (ResolveContext.Options.FieldInitializerScope))
flags |= ResolveContext.Options.FieldInitializerScope;
if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
flags |= ResolveContext.Options.ExpressionTreeConversion;
if (rc.HasSet (ResolveContext.Options.BaseInitializer))
flags |= ResolveContext.Options.BaseInitializer;
}
public ExceptionStatement CurrentTryBlock { get; set; }
public LoopStatement EnclosingLoop { get; set; }
public LoopStatement EnclosingLoopOrSwitch { get; set; }
public Switch Switch { get; set; }
public TypeSpec ReturnType {
get { return return_type; }
}
}
//
// Expression resolving context
//
public class ResolveContext : IMemberContext
{
[Flags]
public enum Options
{
/// <summary>
/// This flag tracks the `checked' state of the compilation,
/// it controls whether we should generate code that does overflow
/// checking, or if we generate code that ignores overflows.
///
/// The default setting comes from the command line option to generate
/// checked or unchecked code plus any source code changes using the
/// checked/unchecked statements or expressions. Contrast this with
/// the ConstantCheckState flag.
/// </summary>
CheckedScope = 1 << 0,
/// <summary>
/// The constant check state is always set to `true' and cant be changed
/// from the command line. The source code can change this setting with
/// the `checked' and `unchecked' statements and expressions.
/// </summary>
ConstantCheckState = 1 << 1,
AllCheckStateFlags = CheckedScope | ConstantCheckState,
//
// unsafe { ... } scope
//
UnsafeScope = 1 << 2,
CatchScope = 1 << 3,
FinallyScope = 1 << 4,
FieldInitializerScope = 1 << 5,
CompoundAssignmentScope = 1 << 6,
FixedInitializerScope = 1 << 7,
BaseInitializer = 1 << 8,
//
// Inside an enum definition, we do not resolve enumeration values
// to their enumerations, but rather to the underlying type/value
// This is so EnumVal + EnumValB can be evaluated.
//
// There is no "E operator + (E x, E y)", so during an enum evaluation
// we relax the rules
//
EnumScope = 1 << 9,
ConstantScope = 1 << 10,
ConstructorScope = 1 << 11,
UsingInitializerScope = 1 << 12,
LockScope = 1 << 13,
TryScope = 1 << 14,
TryWithCatchScope = 1 << 15,
DontSetConditionalAccessReceiver = 1 << 16,
///
/// Indicates the current context is in probing mode, no errors are reported.
///
ProbingMode = 1 << 22,
//
// Return and ContextualReturn statements will set the ReturnType
// value based on the expression types of each return statement
// instead of the method return type which is initially null.
//
InferReturnType = 1 << 23,
OmitDebuggingInfo = 1 << 24,
ExpressionTreeConversion = 1 << 25,
InvokeSpecialName = 1 << 26
}
// utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
// it's public so that we can use a struct at the callsite
public struct FlagsHandle : IDisposable
{
readonly ResolveContext ec;
readonly Options invmask, oldval;
public FlagsHandle (ResolveContext ec, Options flagsToSet)
: this (ec, flagsToSet, flagsToSet)
{
}
internal FlagsHandle (ResolveContext ec, Options mask, Options val)
{
this.ec = ec;
invmask = ~mask;
oldval = ec.flags & mask;
ec.flags = (ec.flags & invmask) | (val & mask);
// if ((mask & Options.ProbingMode) != 0)
// ec.Report.DisableReporting ();
}
public void Dispose ()
{
// if ((invmask & Options.ProbingMode) == 0)
// ec.Report.EnableReporting ();
ec.flags = (ec.flags & invmask) | oldval;
}
}
protected Options flags;
//
// Whether we are inside an anonymous method.
//
public AnonymousExpression CurrentAnonymousMethod;
//
// Holds a varible used during collection or object initialization.
//
public Expression CurrentInitializerVariable;
public Block CurrentBlock;
public readonly IMemberContext MemberContext;
public ResolveContext (IMemberContext mc)
{
if (mc == null)
throw new ArgumentNullException ();
MemberContext = mc;
//
// The default setting comes from the command line option
//
if (mc.Module.Compiler.Settings.Checked)
flags |= Options.CheckedScope;
//
// The constant check state is always set to true
//
flags |= Options.ConstantCheckState;
}
public ResolveContext (IMemberContext mc, Options options)
: this (mc)
{
flags |= options;
}
#region Properties
public BuiltinTypes BuiltinTypes {
get {
return MemberContext.Module.Compiler.BuiltinTypes;
}
}
public virtual ExplicitBlock ConstructorBlock {
get {
return CurrentBlock.Explicit;
}
}
//
// The current iterator
//
public Iterator CurrentIterator {
get { return CurrentAnonymousMethod as Iterator; }
}
public TypeSpec CurrentType {
get { return MemberContext.CurrentType; }
}
public TypeParameters CurrentTypeParameters {
get { return MemberContext.CurrentTypeParameters; }
}
public MemberCore CurrentMemberDefinition {
get { return MemberContext.CurrentMemberDefinition; }
}
public bool ConstantCheckState {
get { return (flags & Options.ConstantCheckState) != 0; }
}
public bool IsInProbingMode {
get {
return (flags & Options.ProbingMode) != 0;
}
}
public bool IsObsolete {
get {
// Disables obsolete checks when probing is on
return MemberContext.IsObsolete;
}
}
public bool IsStatic {
get {
return MemberContext.IsStatic;
}
}
public bool IsUnsafe {
get {
return HasSet (Options.UnsafeScope) || MemberContext.IsUnsafe;
}
}
public bool IsRuntimeBinder {
get {
return Module.Compiler.IsRuntimeBinder;
}
}
public bool IsVariableCapturingRequired {
get {
return !IsInProbingMode;
}
}
public ModuleContainer Module {
get {
return MemberContext.Module;
}
}
public Report Report {
get {
return Module.Compiler.Report;
}
}
#endregion
public bool MustCaptureVariable (INamedBlockVariable local)
{
if (CurrentAnonymousMethod == null)
return false;
//
// Capture only if this or any of child blocks contain yield
// or it's a parameter
//
if (CurrentAnonymousMethod.IsIterator)
return local.IsParameter || local.Block.Explicit.HasYield;
//
// Capture only if this or any of child blocks contain await
// or it's a parameter or we need to access variable from
// different parameter block
//
if (CurrentAnonymousMethod is AsyncInitializer)
return local.IsParameter || local.Block.Explicit.HasAwait || CurrentBlock.Explicit.HasAwait ||
local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
return local.Block.ParametersBlock != CurrentBlock.ParametersBlock.Original;
}
public bool HasSet (Options options)
{
return (this.flags & options) == options;
}
public bool HasAny (Options options)
{
return (this.flags & options) != 0;
}
// Temporarily set all the given flags to the given value. Should be used in an 'using' statement
public FlagsHandle Set (Options options)
{
return new FlagsHandle (this, options);
}
public FlagsHandle With (Options options, bool enable)
{
return new FlagsHandle (this, options, enable ? options : 0);
}
#region IMemberContext Members
public string GetSignatureForError ()
{
return MemberContext.GetSignatureForError ();
}
public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
{
return MemberContext.LookupExtensionMethod (name, arity);
}
public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
{
return MemberContext.LookupNamespaceOrType (name, arity, mode, loc);
}
public FullNamedExpression LookupNamespaceAlias (string name)
{
return MemberContext.LookupNamespaceAlias (name);
}
#endregion
}
public class FlowAnalysisContext
{
readonly CompilerContext ctx;
public FlowAnalysisContext (CompilerContext ctx, ParametersBlock parametersBlock, int definiteAssignmentLength)
{
this.ctx = ctx;
this.ParametersBlock = parametersBlock;
DefiniteAssignment = definiteAssignmentLength == 0 ?
DefiniteAssignmentBitSet.Empty :
new DefiniteAssignmentBitSet (definiteAssignmentLength);
}
public DefiniteAssignmentBitSet DefiniteAssignment { get; set; }
public DefiniteAssignmentBitSet DefiniteAssignmentOnTrue { get; set; }
public DefiniteAssignmentBitSet DefiniteAssignmentOnFalse { get; set; }
Dictionary<Statement, List<DefiniteAssignmentBitSet>> LabelStack { get; set; }
public ParametersBlock ParametersBlock { get; set; }
public Report Report {
get {
return ctx.Report;
}
}
public DefiniteAssignmentBitSet SwitchInitialDefinitiveAssignment { get; set; }
public TryFinally TryFinally { get; set; }
public bool UnreachableReported { get; set; }
public bool AddReachedLabel (Statement label)
{
List<DefiniteAssignmentBitSet> das;
if (LabelStack == null) {
LabelStack = new Dictionary<Statement, List<DefiniteAssignmentBitSet>> ();
das = null;
} else {
LabelStack.TryGetValue (label, out das);
}
if (das == null) {
das = new List<DefiniteAssignmentBitSet> ();
das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
LabelStack.Add (label, das);
return false;
}
foreach (var existing in das) {
if (DefiniteAssignmentBitSet.IsIncluded (existing, DefiniteAssignment))
return true;
}
if (DefiniteAssignment == DefiniteAssignmentBitSet.Empty)
das.Add (DefiniteAssignment);
else
das.Add (new DefiniteAssignmentBitSet (DefiniteAssignment));
return false;
}
public DefiniteAssignmentBitSet BranchDefiniteAssignment ()
{
return BranchDefiniteAssignment (DefiniteAssignment);
}
public DefiniteAssignmentBitSet BranchDefiniteAssignment (DefiniteAssignmentBitSet da)
{
if (da != DefiniteAssignmentBitSet.Empty) {
DefiniteAssignment = new DefiniteAssignmentBitSet (da);
}
return da;
}
public bool IsDefinitelyAssigned (VariableInfo variable)
{
return variable.IsAssigned (DefiniteAssignment);
}
public bool IsStructFieldDefinitelyAssigned (VariableInfo variable, string name)
{
return variable.IsStructFieldAssigned (DefiniteAssignment, name);
}
public void SetVariableAssigned (VariableInfo variable, bool generatedAssignment = false)
{
variable.SetAssigned (DefiniteAssignment, generatedAssignment);
}
public void SetStructFieldAssigned (VariableInfo variable, string name)
{
variable.SetStructFieldAssigned (DefiniteAssignment, name);
}
}
//
// This class is used during the Statement.Clone operation
// to remap objects that have been cloned.
//
// Since blocks are cloned by Block.Clone, we need a way for
// expressions that must reference the block to be cloned
// pointing to the new cloned block.
//
public class CloneContext
{
Dictionary<Block, Block> block_map = new Dictionary<Block, Block> ();
public void AddBlockMap (Block from, Block to)
{
block_map.Add (from, to);
}
public Block LookupBlock (Block from)
{
Block result;
if (!block_map.TryGetValue (from, out result)) {
result = (Block) from.Clone (this);
}
return result;
}
///
/// Remaps block to cloned copy if one exists.
///
public Block RemapBlockCopy (Block from)
{
Block mapped_to;
if (!block_map.TryGetValue (from, out mapped_to))
return from;
return mapped_to;
}
}
//
// Main compiler context
//
public class CompilerContext
{
static readonly TimeReporter DisabledTimeReporter = new TimeReporter (false);
readonly Report report;
readonly BuiltinTypes builtin_types;
readonly CompilerSettings settings;
Dictionary<string, SourceFile> all_source_files;
public CompilerContext (CompilerSettings settings, ReportPrinter reportPrinter)
{
this.settings = settings;
this.report = new Report (this, reportPrinter);
this.builtin_types = new BuiltinTypes ();
this.TimeReporter = DisabledTimeReporter;
}
#region Properties
public BuiltinTypes BuiltinTypes {
get {
return builtin_types;
}
}
// Used for special handling of runtime dynamic context mostly
// by error reporting but also by member accessibility checks
public bool IsRuntimeBinder {
get; set;
}
public Report Report {
get {
return report;
}
}
public CompilerSettings Settings {
get {
return settings;
}
}
public List<SourceFile> SourceFiles {
get {
return settings.SourceFiles;
}
}
internal TimeReporter TimeReporter {
get; set;
}
#endregion
//
// This is used when we encounter a #line preprocessing directive during parsing
// to register additional source file names
//
public SourceFile LookupFile (CompilationSourceFile comp_unit, string name)
{
if (all_source_files == null) {
all_source_files = new Dictionary<string, SourceFile> ();
foreach (var source in SourceFiles)
all_source_files[source.FullPathName] = source;
}
string path;
if (!Path.IsPathRooted (name)) {
var loc = comp_unit.SourceFile;
string root = Path.GetDirectoryName (loc.FullPathName);
path = Path.GetFullPath (Path.Combine (root, name));
var dir = Path.GetDirectoryName (loc.Name);
if (!string.IsNullOrEmpty (dir))
name = Path.Combine (dir, name);
} else
path = name;
SourceFile retval;
if (all_source_files.TryGetValue (path, out retval))
return retval;
retval = new SourceFile (name, path, all_source_files.Count + 1);
Location.AddFile (retval);
all_source_files.Add (path, retval);
return retval;
}
}
//
// Generic code emitter context
//
public class BuilderContext
{
[Flags]
public enum Options
{
/// <summary>
/// This flag tracks the `checked' state of the compilation,
/// it controls whether we should generate code that does overflow
/// checking, or if we generate code that ignores overflows.
///
/// The default setting comes from the command line option to generate
/// checked or unchecked code plus any source code changes using the
/// checked/unchecked statements or expressions. Contrast this with
/// the ConstantCheckState flag.
/// </summary>
CheckedScope = 1 << 0,
AccurateDebugInfo = 1 << 1,
OmitDebugInfo = 1 << 2,
ConstructorScope = 1 << 3,
AsyncBody = 1 << 4,
}
// utility helper for CheckExpr, UnCheckExpr, Checked and Unchecked statements
// it's public so that we can use a struct at the callsite
public struct FlagsHandle : IDisposable
{
readonly BuilderContext ec;
readonly Options invmask, oldval;
public FlagsHandle (BuilderContext ec, Options flagsToSet)
: this (ec, flagsToSet, flagsToSet)
{
}
internal FlagsHandle (BuilderContext ec, Options mask, Options val)
{
this.ec = ec;
invmask = ~mask;
oldval = ec.flags & mask;
ec.flags = (ec.flags & invmask) | (val & mask);
}
public void Dispose ()
{
ec.flags = (ec.flags & invmask) | oldval;
}
}
protected Options flags;
public bool HasSet (Options options)
{
return (this.flags & options) == options;
}
// Temporarily set all the given flags to the given value. Should be used in an 'using' statement
public FlagsHandle With (Options options, bool enable)
{
return new FlagsHandle (this, options, enable ? options : 0);
}
}
//
// Parser session objects. We could recreate all these objects for each parser
// instance but the best parser performance the session object can be reused
//
public class ParserSession
{
MD5 md5;
public readonly char[] StreamReaderBuffer = new char[SeekableStreamReader.DefaultReadAheadSize * 2];
public readonly Dictionary<char[], string>[] Identifiers = new Dictionary<char[], string>[Tokenizer.MaxIdentifierLength + 1];
public readonly List<Parameter> ParametersStack = new List<Parameter> (4);
public readonly char[] IDBuilder = new char[Tokenizer.MaxIdentifierLength];
public readonly char[] NumberBuilder = new char[Tokenizer.MaxNumberLength];
public LocationsBag LocationsBag { get; set; }
public bool UseJayGlobalArrays { get; set; }
public LocatedToken[] LocatedTokens { get; set; }
public MD5 GetChecksumAlgorithm ()
{
return md5 ?? (md5 = MD5.Create ());
}
}
}