Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@@ -0,0 +1,80 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
[DebuggerDisplay("{this.ToString()}")]
internal class BinarySearchResult
{
private int result;
private int count;
internal BinarySearchResult(int resultFromBinarySearch, int count)
{
this.result = resultFromBinarySearch;
this.count = count;
}
internal bool IsFound
{
get { return this.result >= 0; }
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int FoundIndex
{
get
{
UnitTestUtility.Assert(this.IsFound, "We should not call FoundIndex if we cannot find the element.");
return this.result;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal int NextIndex
{
get
{
UnitTestUtility.Assert(!this.IsFound, "We should not call NextIndex if we found the element.");
UnitTestUtility.Assert(this.IsNextIndexAvailable, "We should not call NextIndex if next index is not available.");
return this.NextIndexValue;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal bool IsNextIndexAvailable
{
get
{
UnitTestUtility.Assert(!this.IsFound, "We should not call IsNextIndexAvailable if we found the element.");
return this.NextIndexValue != this.count;
}
}
private int NextIndexValue
{
get { return ~this.result; }
}
[SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object)", Justification = "Message used in debugger only.")]
public override string ToString()
{
if (this.IsFound)
{
return string.Format("Data is found at index {0}.", this.FoundIndex);
}
else if (this.IsNextIndexAvailable)
{
return string.Format("Data is not found, the next index is {0}.", this.NextIndex);
}
else
{
return "Data is not found and there is no next index.";
}
}
}
}

View File

@@ -0,0 +1,41 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Collections.Generic;
internal partial class CharacterSpottingTextReader : ICharacterSpottingTextReaderForUnitTest
{
int ICharacterSpottingTextReaderForUnitTest.CurrentLine
{
get { return this.currentLine; }
}
int ICharacterSpottingTextReaderForUnitTest.CurrentPosition
{
get { return this.currentPosition; }
}
List<DocumentLocation> ICharacterSpottingTextReaderForUnitTest.StartBrackets
{
get { return this.startAngleBrackets; }
}
List<DocumentLocation> ICharacterSpottingTextReaderForUnitTest.EndBrackets
{
get { return this.endAngleBrackets; }
}
List<DocumentLocation> ICharacterSpottingTextReaderForUnitTest.SingleQuotes
{
get { return this.singleQuotes; }
}
List<DocumentLocation> ICharacterSpottingTextReaderForUnitTest.DoubleQuotes
{
get { return this.doubleQuotes; }
}
}
}

View File

@@ -0,0 +1,214 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Collections.Generic;
using System.IO;
//
internal partial class CharacterSpottingTextReader : TextReader
{
// These 'special characters' couple with the fact that we are working on XML.
private const char StartAngleBracket = '<';
private const char EndAngleBracket = '>';
private const char SingleQuote = '\'';
private const char DoubleQuote = '"';
private const char EndLine = '\n';
private const char CarriageReturn = '\r';
private TextReader underlyingReader;
private int currentLine;
private int currentPosition;
private List<DocumentLocation> startAngleBrackets;
private List<DocumentLocation> endAngleBrackets;
private List<DocumentLocation> singleQuotes;
private List<DocumentLocation> doubleQuotes;
private List<DocumentLocation> endLines;
public CharacterSpottingTextReader(TextReader underlyingReader)
{
UnitTestUtility.Assert(underlyingReader != null, "underlyingReader should not be null and should be ensured by caller.");
this.underlyingReader = underlyingReader;
this.currentLine = 1;
this.currentPosition = 1;
this.startAngleBrackets = new List<DocumentLocation>();
this.endAngleBrackets = new List<DocumentLocation>();
this.singleQuotes = new List<DocumentLocation>();
this.doubleQuotes = new List<DocumentLocation>();
this.endLines = new List<DocumentLocation>();
}
// CurrentLocation consists of the current line number and the current position on the line.
//
// The current position is like a cursor moving along the line. For example, a string "abc" ending with "\r\n":
//
// abc\r\n
//
// the current position, depicted as | below, moves from char to char:
//
// |a|b|c|\r|\n
//
// When we are at the beginning of the line, the current position is 1. After we read the first char,
// we advance the current position to 2, and so on:
//
// 1 2 3 4
// |a|b|c|\r|\n
//
// As we reach the end-of-line character on the line, which can be \r, \r\n or \n, we move to the next line and reset the current position to 1.
private DocumentLocation CurrentLocation
{
get
{
return new DocumentLocation(this.currentLine, this.currentPosition);
}
}
public override void Close()
{
this.underlyingReader.Close();
}
public override int Peek()
{
// This character is not consider read, therefore we don't need to analyze this.
return this.underlyingReader.Peek();
}
public override int Read()
{
int result = this.underlyingReader.Read();
if (result != -1)
{
result = this.AnalyzeReadData((char)result);
}
return result;
}
internal DocumentLocation FindCharacterStrictlyAfter(char c, DocumentLocation afterLocation)
{
List<DocumentLocation> locationList = this.GetLocationList(c);
UnitTestUtility.Assert(locationList != null, "We should always find character for special characters only");
// Note that this 'nextLocation' may not represent a real document location (we could hit an end line character here so that there is no next line
// position. This is merely used for the search algorithm below:
DocumentLocation nextLocation = new DocumentLocation(afterLocation.LineNumber, new OneBasedCounter(afterLocation.LinePosition.Value + 1));
BinarySearchResult result = locationList.MyBinarySearch(nextLocation);
if (result.IsFound)
{
// It is possible that the next location is a quote itself, or
return nextLocation;
}
else if (result.IsNextIndexAvailable)
{
// Some other later position is the quote, or
return locationList[result.NextIndex];
}
else
{
// in the worst case no quote can be found.
return null;
}
}
internal DocumentLocation FindCharacterStrictlyBefore(char c, DocumentLocation documentLocation)
{
List<DocumentLocation> locationList = this.GetLocationList(c);
UnitTestUtility.Assert(locationList != null, "We should always find character for special characters only");
BinarySearchResult result = locationList.MyBinarySearch(documentLocation);
if (result.IsFound)
{
if (result.FoundIndex > 0)
{
return locationList[result.FoundIndex - 1];
}
else
{
return null;
}
}
else if (result.IsNextIndexAvailable)
{
if (result.NextIndex > 0)
{
return locationList[result.NextIndex - 1];
}
else
{
return null;
}
}
else if (locationList.Count > 0)
{
return locationList[locationList.Count - 1];
}
else
{
return null;
}
}
private List<DocumentLocation> GetLocationList(char c)
{
switch (c)
{
case StartAngleBracket:
return this.startAngleBrackets;
case EndAngleBracket:
return this.endAngleBrackets;
case SingleQuote:
return this.singleQuotes;
case DoubleQuote:
return this.doubleQuotes;
case EndLine:
return this.endLines;
default:
return null;
}
}
/// <summary>
/// Process last character read, and canonicalize end line.
/// </summary>
/// <param name="lastCharacterRead">The last character read by the underlying reader</param>
/// <returns>The last character processed</returns>
private char AnalyzeReadData(char lastCharacterRead)
{
// XML specification requires end-of-line == '\n' or '\r' or "\r\n"
// See http://www.w3.org/TR/2008/REC-xml-20081126/#sec-line-ends for details.
if (lastCharacterRead == CarriageReturn)
{
// if reading \r and peek next char is \n, then process \n as well
int nextChar = this.underlyingReader.Peek();
if (nextChar == EndLine)
{
lastCharacterRead = (char)this.underlyingReader.Read();
}
}
if (lastCharacterRead == EndLine || lastCharacterRead == CarriageReturn)
{
this.endLines.Add(this.CurrentLocation);
this.currentLine++;
this.currentPosition = 1;
// according to XML spec, both \r\n and \r should be translated to \n
return EndLine;
}
else
{
List<DocumentLocation> locations = this.GetLocationList(lastCharacterRead);
if (locations != null)
{
locations.Add(this.CurrentLocation);
}
this.currentPosition++;
return lastCharacterRead;
}
}
}
}

View File

@@ -0,0 +1,100 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Activities.Hosting;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Runtime;
using System.Activities.Validation;
// DebugController, one is needed per ActivityExecutor.
[DebuggerNonUserCode]
class DebugController
{
WorkflowInstance host;
DebugManager debugManager; // Instantiated after first instrumentation is successful.
public DebugController(WorkflowInstance host)
{
this.host = host;
}
public void WorkflowStarted()
{
}
public void WorkflowCompleted()
{
if (this.debugManager != null)
{
this.debugManager.Exit();
this.debugManager = null;
}
}
public void ActivityStarted(ActivityInstance activityInstance)
{
if (!(activityInstance.Activity.RootActivity is Constraint)) // Don't debug an activity in a Constraint
{
EnsureActivityInstrumented(activityInstance, false);
this.debugManager.OnEnterState(activityInstance);
}
}
public void ActivityCompleted(ActivityInstance activityInstance)
{
if (!(activityInstance.Activity.RootActivity is Constraint)) // Don't debug an activity in a Constraint
{
EnsureActivityInstrumented(activityInstance, true);
this.debugManager.OnLeaveState(activityInstance);
}
}
// Lazy instrumentation.
// Parameter primeCurrentInstance specify whether priming (if needed) is done
// up to the current instance. Set this to true when calling this from an "...Completed"
// (exit state).
void EnsureActivityInstrumented(ActivityInstance instance, bool primeCurrentInstance)
{
if (this.debugManager == null)
{ // Workflow has not been instrumented yet.
// Finding rootInstance and check all referred sources.
Stack<ActivityInstance> ancestors = new Stack<ActivityInstance>();
while (instance.Parent != null)
{
ancestors.Push(instance);
instance = instance.Parent;
}
Activity rootActivity = instance.Activity;
// Do breakOnStartup only if debugger is attached from the beginning, i.e. no priming needed.
// This specified by change the last parameter below to: "(ancestors.Count == 0)".
this.debugManager = new DebugManager(rootActivity, "Workflow", "Workflow", "DebuggerThread", false, this.host, ancestors.Count == 0);
if (ancestors.Count > 0)
{
// Priming the background thread
this.debugManager.IsPriming = true;
while (ancestors.Count > 0)
{
ActivityInstance ancestorInstance = ancestors.Pop();
this.debugManager.OnEnterState(ancestorInstance);
}
if (primeCurrentInstance)
{
this.debugManager.OnEnterState(instance);
}
this.debugManager.IsPriming = false;
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Diagnostics;
// Immutable, lineNumber and linePosition always non-null.
[DebuggerDisplay("({LineNumber.Value}:{LinePosition.Value})")]
internal class DocumentLocation : IEquatable<DocumentLocation>, IComparable<DocumentLocation>
{
private OneBasedCounter lineNumber;
private OneBasedCounter linePosition;
internal DocumentLocation(OneBasedCounter lineNumber, OneBasedCounter linePosition)
{
UnitTestUtility.Assert(lineNumber != null, "lineNumber should not be null.");
UnitTestUtility.Assert(linePosition != null, "linePosition should not be null.");
this.lineNumber = lineNumber;
this.linePosition = linePosition;
}
internal DocumentLocation(int lineNumber, int linePosition)
: this(new OneBasedCounter(lineNumber), new OneBasedCounter(linePosition))
{
}
internal OneBasedCounter LineNumber
{
get { return this.lineNumber; }
}
internal OneBasedCounter LinePosition
{
get { return this.linePosition; }
}
public bool Equals(DocumentLocation that)
{
if (that == null)
{
return false;
}
return (this.lineNumber.Value == that.lineNumber.Value) && (this.linePosition.Value == that.linePosition.Value);
}
public override int GetHashCode()
{
return this.lineNumber.Value.GetHashCode() ^ this.linePosition.Value.GetHashCode();
}
public int CompareTo(DocumentLocation that)
{
if (that == null)
{
// Following the convention we have in System.Int32 that anything is considered bigger than null.
return 1;
}
if (this.lineNumber.Value == that.lineNumber.Value)
{
// The subtraction of two numbers >= 1 must not underflow integer.
return this.linePosition.Value - that.linePosition.Value;
}
else
{
// The subtraction of two numbers >= 1 must not underflow integer.
return this.lineNumber.Value - that.lineNumber.Value;
}
}
}
}

View File

@@ -0,0 +1,55 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Diagnostics;
// Immutable, start and end always non-null.
[DebuggerDisplay("({Start.LineNumber.Value}:{Start.LinePosition.Value}) - ({End.LineNumber.Value}:{End.LinePosition.Value})")]
internal class DocumentRange : IEquatable<DocumentRange>
{
private DocumentLocation start;
private DocumentLocation end;
internal DocumentRange(DocumentLocation start, DocumentLocation end)
{
UnitTestUtility.Assert(start != null, "DocumentRange.Start cannot be null");
UnitTestUtility.Assert(end != null, "DocumentRange.End cannot be null");
UnitTestUtility.Assert((start.LineNumber.Value < end.LineNumber.Value) || ((start.LineNumber.Value == end.LineNumber.Value) && (start.LinePosition.Value <= end.LinePosition.Value)), "Start cannot before go after End.");
this.start = start;
this.end = end;
}
internal DocumentRange(int startLineNumber, int startLinePosition, int endLineNumber, int endLinePosition)
: this(new DocumentLocation(startLineNumber, startLinePosition), new DocumentLocation(endLineNumber, endLinePosition))
{
}
internal DocumentLocation Start
{
get { return this.start; }
}
internal DocumentLocation End
{
get { return this.end; }
}
public bool Equals(DocumentRange other)
{
if (other == null)
{
return false;
}
return this.Start.Equals(other.Start) && this.End.Equals(other.End);
}
public override int GetHashCode()
{
return this.Start.GetHashCode() ^ this.End.GetHashCode();
}
}
}

View File

@@ -0,0 +1,23 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Collections.Generic;
internal interface ICharacterSpottingTextReaderForUnitTest
{
int CurrentLine { get; }
int CurrentPosition { get; }
List<DocumentLocation> StartBrackets { get; }
List<DocumentLocation> EndBrackets { get; }
List<DocumentLocation> SingleQuotes { get; }
List<DocumentLocation> DoubleQuotes { get; }
}
}

View File

@@ -0,0 +1,15 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Debugger
{
// Interface to implement in serializable object containing Workflow
// to be debuggable with Workflow debugger.
public interface IDebuggableWorkflowTree
{
// Return the root of the workflow tree.
Activity GetWorkflowRoot();
}
}

View File

@@ -0,0 +1,116 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Debugger
{
using System.Collections.Generic;
using System.Activities.Debugger.Symbol;
// Keep track of instrumentation information.
// - which subroot has source file but not yet instrumented.
// - which subroots share the same source file
// SubRoot is defined as an activity that has a source file
// (Custom Activity).
class InstrumentationTracker
{
// Root of the workflow to keep track.
Activity root;
// Mapping of subroots to their source files.
Dictionary<Activity, string> uninstrumentedSubRoots;
Dictionary<Activity, string> UninstrumentedSubRoots
{
get
{
if (this.uninstrumentedSubRoots == null)
{
InitializeUninstrumentedSubRoots();
}
return this.uninstrumentedSubRoots;
}
}
public InstrumentationTracker(Activity root)
{
this.root = root;
}
// Initialize UninstrumentedSubRoots by traversing the workflow.
void InitializeUninstrumentedSubRoots()
{
this.uninstrumentedSubRoots = new Dictionary<Activity, string>();
Queue<Activity> activitiesRemaining = new Queue<Activity>();
CollectSubRoot(this.root);
activitiesRemaining.Enqueue(this.root);
while (activitiesRemaining.Count > 0)
{
Activity toProcess = activitiesRemaining.Dequeue();
foreach (Activity activity in WorkflowInspectionServices.GetActivities(toProcess))
{
if (!uninstrumentedSubRoots.ContainsKey(activity))
{
CollectSubRoot(activity);
activitiesRemaining.Enqueue(activity);
}
}
}
}
// Collect subroot as uninstrumented activity.
void CollectSubRoot(Activity activity)
{
string wfSymbol = DebugSymbol.GetSymbol(activity) as string;
if (!string.IsNullOrEmpty(wfSymbol))
{
this.uninstrumentedSubRoots.Add(activity, wfSymbol);
}
else
{
string sourcePath = XamlDebuggerXmlReader.GetFileName(activity) as string;
if (!string.IsNullOrEmpty(sourcePath))
{
this.uninstrumentedSubRoots.Add(activity, sourcePath);
}
}
}
// Whether this is unistrumented sub root.
public bool IsUninstrumentedSubRoot(Activity subRoot)
{
return this.UninstrumentedSubRoots.ContainsKey(subRoot);
}
// Returns Activities that have the same source as the given subRoot.
// This will return other instantiation of the same custom activity.
// Needed to avoid re-instrumentation of the same file.
public List<Activity> GetSameSourceSubRoots(Activity subRoot)
{
string sourcePath;
List<Activity> sameSourceSubRoots = new List<Activity>();
if (this.UninstrumentedSubRoots.TryGetValue(subRoot, out sourcePath))
{
foreach (KeyValuePair<Activity, string> entry in this.UninstrumentedSubRoots)
{
if (entry.Value == sourcePath && entry.Key != subRoot)
{
sameSourceSubRoots.Add(entry.Key);
}
}
}
return sameSourceSubRoots;
}
// Mark this sub root as instrumented.
public void MarkInstrumented(Activity subRoot)
{
this.UninstrumentedSubRoots.Remove(subRoot);
}
}
}

View File

@@ -0,0 +1,16 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
using System.Collections.Generic;
internal static class ListExtensions
{
internal static BinarySearchResult MyBinarySearch<T>(this List<T> input, T item)
{
return new BinarySearchResult(input.BinarySearch(item), input.Count);
}
}
}

View File

@@ -0,0 +1,44 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
// Class to describe the name and type for an early bound local
// that will show up in Locals window.
[DebuggerNonUserCode]
[Fx.Tag.XamlVisible(false)]
public class LocalsItemDescription
{
public LocalsItemDescription(string name, Type type)
{
this.Name = name;
this.Type = type;
}
public string Name
{
get;
private set;
}
[SuppressMessage(FxCop.Category.Naming, FxCop.Rule.PropertyNamesShouldNotMatchGetMethods,
Justification = "Workflow normalizes on Type for Type properties")]
public Type Type
{
get;
private set;
}
public override string ToString()
{
return this.Name + ":" + this.Type.ToString();
}
}
}

View File

@@ -0,0 +1,23 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
// Immutable, value >= 1
internal class OneBasedCounter
{
private int value;
internal OneBasedCounter(int value)
{
UnitTestUtility.Assert(value > 0, "value cannot less than one for OneBasedCounter");
this.value = value;
}
internal int Value
{
get { return this.value; }
}
}
}

View File

@@ -0,0 +1,198 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Activities.Debugger.Symbol;
using System.Diagnostics;
using System.Linq;
using System.Runtime;
// Identifies a specific location in the target source code.
//
// This source information is used in creating PDBs, which will be passed to the debugger,
// which will resolve the source file based off its own source paths.
// Source ranges can:
// * refer to just an entire single line.
// * can be a subset within a single line (when StartLine == EndLine)
// * can also span multiple lines.
// When column info is provided, the debugger will highlight the characters starting at the start line and start column,
// and going up to but not including the character specified by the end line and end column.
[Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Our partial trust mechanisms require that this class remain Immutable. Do not add code that allows an instance of this class to change after creation without strict review.")]
[DebuggerNonUserCode]
[Serializable]
[Fx.Tag.XamlVisible(false)]
public class SourceLocation
{
string fileName;
int startLine;
int endLine;
int startColumn;
int endColumn;
byte[] checksum;
// Define a source location from a filename and line-number (1-based).
// This is a convenience constructor to specify the entire line.
// This does not load the source file to determine column ranges.
public SourceLocation(string fileName, int line)
: this(fileName, line, 1, line, int.MaxValue)
{
}
public SourceLocation(
string fileName,
int startLine,
int startColumn,
int endLine,
int endColumn)
: this(fileName, null, startLine, startColumn, endLine, endColumn)
{
}
// Define a source location in a file.
// Line/Column are 1-based.
internal SourceLocation(
string fileName,
byte[] checksum,
int startLine,
int startColumn,
int endLine,
int endColumn)
{
if (startLine <= 0)
{
throw FxTrace.Exception.Argument("startLine", SR.InvalidSourceLocationLineNumber("startLine", startLine));
}
if (startColumn <= 0)
{
throw FxTrace.Exception.Argument("startColumn", SR.InvalidSourceLocationColumn("startColumn", startColumn));
}
if (endLine <= 0)
{
throw FxTrace.Exception.Argument("endLine", SR.InvalidSourceLocationLineNumber("endLine", endLine));
}
if (endColumn <= 0)
{
throw FxTrace.Exception.Argument("endColumn", SR.InvalidSourceLocationColumn("endColumn", endColumn));
}
if (startLine > endLine)
{
throw FxTrace.Exception.ArgumentOutOfRange("endLine", endLine, SR.OutOfRangeSourceLocationEndLine(startLine));
}
if ((startLine == endLine) && (startColumn > endColumn))
{
throw FxTrace.Exception.ArgumentOutOfRange("endColumn", endColumn, SR.OutOfRangeSourceLocationEndColumn(startColumn));
}
this.fileName = (fileName != null) ? fileName.ToUpperInvariant() : null;
this.startLine = startLine;
this.endLine = endLine;
this.startColumn = startColumn;
this.endColumn = endColumn;
this.checksum = checksum;
}
public string FileName
{
get { return this.fileName; }
}
// Get the 1-based start line.
public int StartLine
{
get { return this.startLine; }
}
// Get the 1-based starting column.
public int StartColumn
{
get { return this.startColumn; }
}
// Get the 1-based end line. This should be greater or equal to StartLine.
public int EndLine
{
get { return this.endLine; }
}
// Get the 1-based ending column.
public int EndColumn
{
get { return this.endColumn; }
}
// get the checksum of the source file
internal byte[] Checksum
{
get { return this.checksum; }
}
public bool IsSingleWholeLine
{
get
{
return this.endColumn == int.MaxValue && this.startLine == this.endLine && this.startColumn == 1;
}
}
// Equality comparison function. This checks for strict equality and
// not for superset or subset relationships.
public override bool Equals(object obj)
{
SourceLocation rsl = obj as SourceLocation;
if (rsl == null)
{
return false;
}
if (this.FileName != rsl.FileName)
{
return false;
}
if (this.StartLine != rsl.StartLine ||
this.StartColumn != rsl.StartColumn ||
this.EndLine != rsl.EndLine ||
this.EndColumn != rsl.EndColumn)
{
return false;
}
if (this.Checksum == null ^ rsl.Checksum == null)
{
return false;
}
else if ((this.Checksum != null && rsl.Checksum != null) && !this.Checksum.SequenceEqual(rsl.Checksum))
{
return false;
}
// everything matches
return true;
}
// Get a hash code.
public override int GetHashCode()
{
return (string.IsNullOrEmpty(this.FileName) ? 0 : this.FileName.GetHashCode()) ^
this.StartLine.GetHashCode() ^
this.StartColumn.GetHashCode() ^
((this.Checksum == null) ? 0 : SymbolHelper.GetHexStringFromChecksum(this.Checksum).GetHashCode());
}
internal static bool IsValidRange(int startLine, int startColumn, int endLine, int endColumn)
{
return
(startLine > 0) && (startColumn > 0) && (endLine > 0) && (endColumn > 0) &&
((startLine < endLine) || (startLine == endLine) && (startColumn < endColumn));
}
}
}

View File

@@ -0,0 +1,42 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
public sealed class SourceLocationFoundEventArgs : EventArgs
{
private object target;
private SourceLocation sourceLocation;
private bool isValueNode;
public SourceLocationFoundEventArgs(object target, SourceLocation sourceLocation)
{
UnitTestUtility.Assert(target != null, "Target cannot be null and is ensured by caller");
UnitTestUtility.Assert(sourceLocation != null, "Target cannot be null and is ensured by caller");
this.target = target;
this.sourceLocation = sourceLocation;
}
internal SourceLocationFoundEventArgs(object target, SourceLocation sourceLocation, bool isValueNode)
: this(target, sourceLocation)
{
this.isValueNode = isValueNode;
}
public object Target
{
get { return this.target; }
}
public SourceLocation SourceLocation
{
get { return this.sourceLocation; }
}
internal bool IsValueNode
{
get { return this.isValueNode; }
}
}
}

View File

@@ -0,0 +1,14 @@
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.Activities.Debugger
{
internal enum SourceLocationMemberType
{
StartLine,
StartColumn,
EndLine,
EndColumn
}
}

View File

@@ -0,0 +1,415 @@
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Activities.Hosting;
using System.Activities.XamlIntegration;
using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Xaml;
using System.Xml;
using System.IO;
using System.Activities.Validation;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Activities.Debugger.Symbol;
using System.Globalization;
// Provide SourceLocation information for activities in given root activity.
// This is integration point with Workflow project system (TBD).
// The current plan is to get SourceLocation from (in this order):
// 1. pdb (when available)
// 2a. parse xaml files available in the same project (or metadata store) or
// 2b. ask user to point to the correct xaml source.
// 3. Publish (serialize to tmp file) and deserialize it to collect SourceLocation (for loose xaml).
// Current code cover only step 3.
[DebuggerNonUserCode]
public static class SourceLocationProvider
{
[Fx.Tag.Throws(typeof(Exception), "Calls Serialize/Deserialize to temporary file")]
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.DoNotCatchGeneralExceptionTypes,
Justification = "We catch all exceptions to avoid leaking security sensitive information.")]
[SuppressMessage(FxCop.Category.Security, "CA2103:ReviewImperativeSecurity",
Justification = "This is security reviewed.")]
[SuppressMessage(FxCop.Category.Security, FxCop.Rule.SecureAsserts,
Justification = "The Assert is only enforce while reading the file and the contents is not leaked.")]
[SuppressMessage("Reliability", "Reliability108:IsFatalRule",
Justification = "We catch all exceptions to avoid leaking security sensitive information.")]
[Fx.Tag.SecurityNote(Critical = "Asserting FileIOPermission(Read) for the specified file name that is contained the attached property on the XAML.",
Safe = "We are not exposing the contents of the file.")]
[SecuritySafeCritical]
static internal Dictionary<object, SourceLocation> GetSourceLocations(Activity rootActivity, out string sourcePath, out bool isTemporaryFile, out byte[] checksum)
{
isTemporaryFile = false;
checksum = null;
string symbolString = DebugSymbol.GetSymbol(rootActivity) as String;
if (string.IsNullOrEmpty(symbolString) && rootActivity.Children != null && rootActivity.Children.Count > 0)
{ // In case of actual root is wrapped either in x:Class activity or CorrelationScope
Activity body = rootActivity.Children[0];
string bodySymbolString = DebugSymbol.GetSymbol(body) as String;
if (!string.IsNullOrEmpty(bodySymbolString))
{
rootActivity = body;
symbolString = bodySymbolString;
}
}
if (!string.IsNullOrEmpty(symbolString))
{
try
{
WorkflowSymbol wfSymbol = WorkflowSymbol.Decode(symbolString);
if (wfSymbol != null)
{
sourcePath = wfSymbol.FileName;
checksum = wfSymbol.GetChecksum();
// rootActivity is the activity with the attached symbol string.
// rootActivity.RootActivity is the workflow root activity.
// if they are not the same, then it must be compiled XAML, because loose XAML (i.e. XAMLX) always have the symbol attached at the root.
if (rootActivity.RootActivity != rootActivity)
{
Fx.Assert(rootActivity.Parent != null, "Compiled XAML implementation always have a parent.");
rootActivity = rootActivity.Parent;
}
return GetSourceLocations(rootActivity, wfSymbol, translateInternalActivityToOrigin: false);
}
}
catch (SerializationException)
{
// Ignore invalid symbol.
}
}
sourcePath = XamlDebuggerXmlReader.GetFileName(rootActivity) as string;
Dictionary<object, SourceLocation> mapping;
Assembly localAssembly;
bool permissionRevertNeeded = false;
// This may not be the local assembly since it may not be the real root for x:Class
localAssembly = rootActivity.GetType().Assembly;
if (rootActivity.Parent != null)
{
localAssembly = rootActivity.Parent.GetType().Assembly;
}
if (rootActivity.Children != null && rootActivity.Children.Count > 0)
{ // In case of actual root is wrapped either in x:Class activity or CorrelationScope
Activity body = rootActivity.Children[0];
string bodySourcePath = XamlDebuggerXmlReader.GetFileName(body) as string;
if (!string.IsNullOrEmpty(bodySourcePath))
{
rootActivity = body;
sourcePath = bodySourcePath;
}
}
try
{
Fx.Assert(!string.IsNullOrEmpty(sourcePath), "If sourcePath is null, it should have been short-circuited before reaching here.");
SourceLocation tempSourceLocation;
Activity tempRootActivity;
checksum = SymbolHelper.CalculateChecksum(sourcePath);
if (TryGetSourceLocation(rootActivity, sourcePath, checksum, out tempSourceLocation)) // already has source location.
{
tempRootActivity = rootActivity;
}
else
{
byte[] buffer;
// Need to store the file in memory temporary so don't have to re-read the file twice
// for XamlDebugXmlReader's BracketLocator.
// If there is a debugger attached, Assert FileIOPermission for Read access to the specific file.
if (System.Diagnostics.Debugger.IsAttached)
{
permissionRevertNeeded = true;
FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Read, sourcePath);
permission.Assert();
}
try
{
FileInfo fi = new FileInfo(sourcePath);
buffer = new byte[fi.Length];
using (FileStream fs = new FileStream(sourcePath, FileMode.Open, FileAccess.Read))
{
fs.Read(buffer, 0, buffer.Length);
}
}
finally
{
// If we Asserted FileIOPermission, revert it.
if (permissionRevertNeeded)
{
CodeAccessPermission.RevertAssert();
permissionRevertNeeded = false;
}
}
object deserializedObject = Deserialize(buffer, localAssembly);
IDebuggableWorkflowTree debuggableWorkflowTree = deserializedObject as IDebuggableWorkflowTree;
if (debuggableWorkflowTree != null)
{ // Declarative Service and x:Class case
tempRootActivity = debuggableWorkflowTree.GetWorkflowRoot();
}
else
{ // Loose XAML case.
tempRootActivity = deserializedObject as Activity;
}
Fx.Assert(tempRootActivity != null, "Unexpected workflow xaml file");
}
mapping = new Dictionary<object, SourceLocation>();
if (tempRootActivity != null)
{
CollectMapping(rootActivity, tempRootActivity, mapping, sourcePath, checksum);
}
}
catch (Exception)
{
// Only eat the exception if we were running in partial trust.
if (!PartialTrustHelpers.AppDomainFullyTrusted)
{
// Eat the exception and return an empty dictionary.
return new Dictionary<object, SourceLocation>();
}
else
{
throw;
}
}
return mapping;
}
public static Dictionary<object, SourceLocation> GetSourceLocations(Activity rootActivity, WorkflowSymbol symbol)
{
return GetSourceLocations(rootActivity, symbol, translateInternalActivityToOrigin: true);
}
// For most of the time, we need source location for object that appear on XAML.
// During debugging, however, we must not transform the internal activity to their origin to make sure it stop when the internal activity is about the execute
// Therefore, in debugger scenario, translateInternalActivityToOrigin will be set to false.
internal static Dictionary<object, SourceLocation> GetSourceLocations(Activity rootActivity, WorkflowSymbol symbol, bool translateInternalActivityToOrigin)
{
Activity workflowRoot = rootActivity.RootActivity ?? rootActivity;
if (!workflowRoot.IsMetadataFullyCached)
{
IList<ValidationError> validationErrors = null;
ActivityUtilities.CacheRootMetadata(workflowRoot, new ActivityLocationReferenceEnvironment(), ProcessActivityTreeOptions.ValidationOptions, null, ref validationErrors);
}
Dictionary<object, SourceLocation> newMapping = new Dictionary<object, SourceLocation>();
// Make sure the qid we are using to TryGetElementFromRoot
// are shifted appropriately such that the first digit that QID is
// the same as the last digit of the rootActivity.QualifiedId.
int[] rootIdArray = rootActivity.QualifiedId.AsIDArray();
int idOffset = rootIdArray[rootIdArray.Length - 1] - 1;
foreach (ActivitySymbol actSym in symbol.Symbols)
{
QualifiedId qid = new QualifiedId(actSym.QualifiedId);
if (idOffset != 0)
{
int[] idArray = qid.AsIDArray();
idArray[0] += idOffset;
qid = new QualifiedId(idArray);
}
Activity activity;
if (QualifiedId.TryGetElementFromRoot(rootActivity, qid, out activity))
{
object origin = activity;
if (translateInternalActivityToOrigin && activity.Origin != null)
{
origin = activity.Origin;
}
newMapping.Add(origin,
new SourceLocation(symbol.FileName, symbol.GetChecksum(), actSym.StartLine, actSym.StartColumn, actSym.EndLine, actSym.EndColumn));
}
}
return newMapping;
}
[Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - We are deserializing XAML from a file. The file may have been read under and Assert for FileIOPermission. The data hould be validated and not cached.")]
internal static object Deserialize(byte[] buffer, Assembly localAssembly)
{
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (TextReader streamReader = new StreamReader(memoryStream))
{
using (XamlDebuggerXmlReader xamlDebuggerReader = new XamlDebuggerXmlReader(streamReader, new XamlSchemaContext(), localAssembly))
{
xamlDebuggerReader.SourceLocationFound += XamlDebuggerXmlReader.SetSourceLocation;
using (XamlReader activityBuilderReader = ActivityXamlServices.CreateBuilderReader(xamlDebuggerReader))
{
return XamlServices.Load(activityBuilderReader);
}
}
}
}
}
public static void CollectMapping(Activity rootActivity1, Activity rootActivity2, Dictionary<object, SourceLocation> mapping, string path)
{
CollectMapping(rootActivity1, rootActivity2, mapping, path, null, requirePrepareForRuntime: true);
}
// Collect mapping for activity1 and its descendants to their corresponding source location.
// activity2 is the shadow of activity1 but with SourceLocation information.
[Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - We are dealing with activity and SourceLocation information that came from the user, possibly under an Assert for FileIOPermission. The data hould be validated and not cached.")]
static void CollectMapping(Activity rootActivity1, Activity rootActivity2, Dictionary<object, SourceLocation> mapping, string path, byte[] checksum, bool requirePrepareForRuntime)
{
// For x:Class, the rootActivity here may not be the real root, but it's the first child of the x:Class activity.
Activity realRoot1 = (rootActivity1.RootActivity != null) ? rootActivity1.RootActivity : rootActivity1;
if ((requirePrepareForRuntime && !realRoot1.IsRuntimeReady) || (!requirePrepareForRuntime && !realRoot1.IsMetadataFullyCached))
{
IList<ValidationError> validationErrors = null;
ActivityUtilities.CacheRootMetadata(realRoot1, new ActivityLocationReferenceEnvironment(), ProcessActivityTreeOptions.ValidationOptions, null, ref validationErrors);
}
// Similarly for rootActivity2.
Activity realRoot2 = (rootActivity2.RootActivity != null) ? rootActivity2.RootActivity : rootActivity2;
if (rootActivity1 != rootActivity2 && (requirePrepareForRuntime && !realRoot2.IsRuntimeReady) || (!requirePrepareForRuntime && !realRoot2.IsMetadataFullyCached))
{
IList<ValidationError> validationErrors = null;
ActivityUtilities.CacheRootMetadata(realRoot2, new ActivityLocationReferenceEnvironment(), ProcessActivityTreeOptions.ValidationOptions, null, ref validationErrors);
}
Queue<KeyValuePair<Activity, Activity>> pairsRemaining = new Queue<KeyValuePair<Activity, Activity>>();
pairsRemaining.Enqueue(new KeyValuePair<Activity, Activity>(rootActivity1, rootActivity2));
KeyValuePair<Activity, Activity> currentPair;
HashSet<Activity> visited = new HashSet<Activity>();
while (pairsRemaining.Count > 0)
{
currentPair = pairsRemaining.Dequeue();
Activity activity1 = currentPair.Key;
Activity activity2 = currentPair.Value;
visited.Add(activity1);
SourceLocation sourceLocation;
if (TryGetSourceLocation(activity2, path, checksum, out sourceLocation))
{
mapping.Add(activity1, sourceLocation);
}
else if (!((activity2 is IExpressionContainer) || (activity2 is IValueSerializableExpression))) // Expression is known not to have source location.
{
//Some activities may not have corresponding Xaml node, e.g. ActivityFaultedOutput.
Trace.WriteLine("WorkflowDebugger: Does not have corresponding Xaml node for: " + activity2.DisplayName + "\n");
}
// This to avoid comparing any value expression with DesignTimeValueExpression (in designer case).
if (!((activity1 is IExpressionContainer) || (activity2 is IExpressionContainer) ||
(activity1 is IValueSerializableExpression) || (activity2 is IValueSerializableExpression)))
{
IEnumerator<Activity> enumerator1 = WorkflowInspectionServices.GetActivities(activity1).GetEnumerator();
IEnumerator<Activity> enumerator2 = WorkflowInspectionServices.GetActivities(activity2).GetEnumerator();
bool hasNextItem1 = enumerator1.MoveNext();
bool hasNextItem2 = enumerator2.MoveNext();
while (hasNextItem1 && hasNextItem2)
{
if (!visited.Contains(enumerator1.Current)) // avoid adding the same activity (e.g. some default implementation).
{
if (enumerator1.Current.GetType() != enumerator2.Current.GetType())
{
// Give debugger log instead of just asserting; to help user find out mismatch problem.
Trace.WriteLine(
"Unmatched type: " + enumerator1.Current.GetType().FullName +
" vs " + enumerator2.Current.GetType().FullName + "\n");
}
pairsRemaining.Enqueue(new KeyValuePair<Activity, Activity>(enumerator1.Current, enumerator2.Current));
}
hasNextItem1 = enumerator1.MoveNext();
hasNextItem2 = enumerator2.MoveNext();
}
// If enumerators do not finish at the same time, then they have unmatched number of activities.
// Give debugger log instead of just asserting; to help user find out mismatch problem.
if (hasNextItem1 || hasNextItem2)
{
Trace.WriteLine("Unmatched number of children\n");
}
}
}
}
static void CollectMapping(Activity rootActivity1, Activity rootActivity2, Dictionary<object, SourceLocation> mapping, string path, byte[] checksum)
{
CollectMapping(rootActivity1, rootActivity2, mapping, path, checksum, requirePrepareForRuntime: true);
}
// Get SourceLocation for object deserialized with XamlDebuggerXmlReader in deserializer stack.
static bool TryGetSourceLocation(object obj, string path, byte[] checksum, out SourceLocation sourceLocation)
{
sourceLocation = null;
int startLine, startColumn, endLine, endColumn;
if (AttachablePropertyServices.TryGetProperty<int>(obj, XamlDebuggerXmlReader.StartLineName, out startLine) &&
AttachablePropertyServices.TryGetProperty<int>(obj, XamlDebuggerXmlReader.StartColumnName, out startColumn) &&
AttachablePropertyServices.TryGetProperty<int>(obj, XamlDebuggerXmlReader.EndLineName, out endLine) &&
AttachablePropertyServices.TryGetProperty<int>(obj, XamlDebuggerXmlReader.EndColumnName, out endColumn) &&
SourceLocation.IsValidRange(startLine, startColumn, endLine, endColumn))
{
sourceLocation = new SourceLocation(path, checksum, startLine, startColumn, endLine, endColumn);
return true;
}
return false;
}
public static ICollection<ActivitySymbol> GetSymbols(Activity rootActivity, Dictionary<object, SourceLocation> sourceLocations)
{
List<ActivitySymbol> symbols = new List<ActivitySymbol>();
Activity realRoot = (rootActivity.RootActivity != null) ? rootActivity.RootActivity : rootActivity;
if (!realRoot.IsMetadataFullyCached)
{
IList<ValidationError> validationErrors = null;
ActivityUtilities.CacheRootMetadata(realRoot, new ActivityLocationReferenceEnvironment(), ProcessActivityTreeOptions.ValidationOptions, null, ref validationErrors);
}
Queue<Activity> activitiesRemaining = new Queue<Activity>();
activitiesRemaining.Enqueue(realRoot);
HashSet<Activity> visited = new HashSet<Activity>();
while (activitiesRemaining.Count > 0)
{
Activity currentActivity = activitiesRemaining.Dequeue();
SourceLocation sourceLocation;
object origin = currentActivity.Origin == null ? currentActivity : currentActivity.Origin;
if (!visited.Contains(currentActivity) && sourceLocations.TryGetValue(origin, out sourceLocation))
{
symbols.Add(new ActivitySymbol
{
QualifiedId = currentActivity.QualifiedId.AsByteArray(),
StartLine = sourceLocation.StartLine,
StartColumn = sourceLocation.StartColumn,
EndLine = sourceLocation.EndLine,
EndColumn = sourceLocation.EndColumn
});
}
visited.Add(currentActivity);
foreach (Activity childActivity in WorkflowInspectionServices.GetActivities(currentActivity))
{
activitiesRemaining.Enqueue(childActivity);
}
}
return symbols;
}
}
}

View File

@@ -0,0 +1,259 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Debugger
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime;
using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Text;
using System.IO;
using System.Globalization;
// Describes a "state" in the interpretter. A state is any source location that
// a breakpoint could be set on or that could be stepped to.
[DebuggerNonUserCode]
[Fx.Tag.XamlVisible(false)]
public class State
{
[Fx.Tag.SecurityNote(Critical = "This value is used in IL generation performed under an assert. It gets validated before setting in partial trust.")]
[SecurityCritical]
SourceLocation location;
[Fx.Tag.SecurityNote(Critical = "This value is used in IL generation performed under an assert. It gets validated before setting in partial trust.")]
[SecurityCritical]
string name;
IEnumerable<LocalsItemDescription> earlyLocals;
int numberOfEarlyLocals;
// Calling Type.GetMethod() is slow (10,000 calls can take ~1 minute).
// So we stash extra fields to be able to make the call lazily (as we Enter the state).
// this.type.GetMethod
Type type;
[Fx.Tag.SecurityNote(Critical = "This value is used in IL generation performed under an assert. It gets validated before setting in partial trust.")]
[SecurityCritical]
string methodName;
[Fx.Tag.SecurityNote(Critical = "This value is used in IL generation performed under an assert. Used to determine if we should invoke the generated code for this state.")]
[SecurityCritical]
bool debuggingEnabled = true;
[Fx.Tag.SecurityNote(Critical = "Sets SecurityCritical name member.",
Safe = "We validate the SourceLocation and name before storing it in the member when running in Partial Trust.")]
[SecuritySafeCritical]
internal State(SourceLocation location, string name, IEnumerable<LocalsItemDescription> earlyLocals, int numberOfEarlyLocals)
{
// If we are running in Partial Trust, validate the name string. We only do this in partial trust for backward compatability.
// We are doing the validation because we want to prevent anything passed to us by non-critical code from affecting the generation
// of the code to the dynamic assembly we are creating.
if (!PartialTrustHelpers.AppDomainFullyTrusted)
{
this.name = ValidateIdentifierString(name);
this.location = ValidateSourceLocation(location);
}
else
{
this.location = location;
this.name = name;
}
this.earlyLocals = earlyLocals;
Fx.Assert(earlyLocals != null || numberOfEarlyLocals == 0,
"If earlyLocals is null then numberOfEarlyLocals should be 0");
// Ignore the passed numberOfEarlyLocals if earlyLocal is null.
this.numberOfEarlyLocals = (earlyLocals == null) ? 0 : numberOfEarlyLocals;
}
// Location in source file associated with this state.
internal SourceLocation Location
{
[Fx.Tag.SecurityNote(Critical = "Accesses the SecurityCritical location member. We validated the location when this object was constructed.",
Safe = "SourceLocation is immutable and we validated it in the constructor.")]
[SecuritySafeCritical]
get { return this.location; }
}
// Friendly name of the state. May be null if state is not named.
// States need unique names.
internal string Name
{
[Fx.Tag.SecurityNote(Critical = "Sets SecurityCritical name member.",
Safe = "We are only reading it, not setting it.")]
[SecuritySafeCritical]
get { return this.name; }
}
// Type definitions for early bound locals. This list is ordered.
// Names should be unique.
internal IEnumerable<LocalsItemDescription> EarlyLocals
{
get { return this.earlyLocals; }
}
internal int NumberOfEarlyLocals
{
get { return this.numberOfEarlyLocals; }
}
internal bool DebuggingEnabled
{
[Fx.Tag.SecurityNote(Critical = "Accesses SecurityCritical debuggingEnabled member.",
Safe = "We don't change anyting. We only return the value.")]
[SecuritySafeCritical]
get
{
return this.debuggingEnabled;
}
[Fx.Tag.SecurityNote(Critical = "Sets SecurityCritical debuggingEnabled member.")]
[SecuritySafeCritical]
set
{
this.debuggingEnabled = value;
}
}
[Fx.Tag.SecurityNote(Critical = "Sets SecurityCritical methodName member.")]
[SecurityCritical]
internal void CacheMethodInfo(Type type, string methodName)
{
this.type = type;
this.methodName = methodName;
}
// Helper to lazily get the MethodInfo. This is expensive, so caller should cache it.
[Fx.Tag.SecurityNote(Critical = "Generates and returns a MethodInfo that is used to generate the dynamic module and accesses Critical member methodName.")]
[SecurityCritical]
internal MethodInfo GetMethodInfo(bool withPriming)
{
MethodInfo methodInfo = this.type.GetMethod(withPriming ? StateManager.MethodWithPrimingPrefix + this.methodName : this.methodName);
return methodInfo;
}
// internal because it is used from StateManager, too for the assembly name, type name, and type name prefix.
internal static string ValidateIdentifierString(string input)
{
string result = input.Normalize(NormalizationForm.FormC);
if (result.Length > 255)
{
result = result.Substring(0, 255);
}
// Make the identifier conform to Unicode programming language identifer specification.
char[] chars = result.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
UnicodeCategory category = char.GetUnicodeCategory(chars[i]);
// Check for identifier_start
if ((category == UnicodeCategory.UppercaseLetter) ||
(category == UnicodeCategory.LowercaseLetter) ||
(category == UnicodeCategory.TitlecaseLetter) ||
(category == UnicodeCategory.ModifierLetter) ||
(category == UnicodeCategory.OtherLetter) ||
(category == UnicodeCategory.LetterNumber))
{
continue;
}
// If it's not the first character, also check for identifier_extend
if ((i != 0) &&
((category == UnicodeCategory.NonSpacingMark) ||
(category == UnicodeCategory.SpacingCombiningMark) ||
(category == UnicodeCategory.DecimalDigitNumber) ||
(category == UnicodeCategory.ConnectorPunctuation) ||
(category == UnicodeCategory.Format)))
{
continue;
}
// Not valid for identifiers - change it to an underscore.
chars[i] = '_';
}
result = new string(chars);
return result;
}
[Fx.Tag.SecurityNote(Critical = "Calls SecurityCritical method StateManager.DisableCodeGeneration.")]
[SecurityCritical]
SourceLocation ValidateSourceLocation(SourceLocation input)
{
bool returnNewLocation = false;
string newFileName = input.FileName;
if (string.IsNullOrWhiteSpace(newFileName))
{
this.DebuggingEnabled = false;
Trace.WriteLine(SR.DebugInstrumentationFailed(SR.InvalidFileName(this.name)));
return input;
}
// There was some validation of the column and line number already done in the SourceLocation constructor.
// We are going to limit line and column numbers to Int16.MaxValue
if ((input.StartLine > Int16.MaxValue) || (input.EndLine > Int16.MaxValue))
{
this.DebuggingEnabled = false;
Trace.WriteLine(SR.DebugInstrumentationFailed(SR.LineNumberTooLarge(this.name)));
return input;
}
if ((input.StartColumn > Int16.MaxValue) || (input.EndColumn > Int16.MaxValue))
{
this.DebuggingEnabled = false;
Trace.WriteLine(SR.DebugInstrumentationFailed(SR.ColumnNumberTooLarge(this.name)));
return input;
}
// Truncate at 255 characters.
if (newFileName.Length > 255)
{
newFileName = newFileName.Substring(0, 255);
returnNewLocation = true;
}
if (ReplaceInvalidCharactersWithUnderscore(ref newFileName, Path.GetInvalidPathChars()))
{
returnNewLocation = true;
}
string fileNameOnly = Path.GetFileName(newFileName);
if (ReplaceInvalidCharactersWithUnderscore(ref fileNameOnly, Path.GetInvalidFileNameChars()))
{
// The filename portion has been munged. We need to make a new full name.
string path = Path.GetDirectoryName(newFileName);
newFileName = path + "\\" + fileNameOnly;
returnNewLocation = true;
}
if (returnNewLocation)
{
return new SourceLocation(newFileName, input.StartLine, input.StartColumn, input.EndLine, input.EndColumn);
}
return input;
}
static bool ReplaceInvalidCharactersWithUnderscore(ref string input, char[] invalidChars)
{
bool modified = false;
int invalidIndex = 0;
while ((invalidIndex = input.IndexOfAny(invalidChars)) != -1)
{
char[] charArray = input.ToCharArray();
charArray[invalidIndex] = '_';
input = new string(charArray);
modified = true;
}
return modified;
}
}
}

Some files were not shown because too many files have changed in this diff Show More