|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//---------------------------------------------------------------------------
//
// Description:
// Markup Compiler class that compiles the markup in a Xaml file into a
// binary stream (Baml) and\or code (IL) in an assembly.
//
//---------------------------------------------------------------------------
#pragma warning disable 1634, 1691
using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using MS.Internal.Markup;
using MS.Internal.Tasks;
using MS.Utility; // for SR
using Microsoft.Build.Utilities;
using Microsoft.Build.Tasks.Windows;
namespace MS.Internal
{
internal sealed class MarkupCompiler
{
#region ExternalAPI
#region Public Properties
///<summary>String that specifies the directory to place the generated files into</summary>
public string TargetPath
{
get { return _targetPath; }
set { _targetPath = value;}
}
/// <summary>
/// The version of the assembly
/// </summary>
public string AssemblyVersion
{
get { return _assemblyVersion; }
set { _assemblyVersion = value; }
}
/// <summary>
/// The public key token of the assembly
/// </summary>
public string AssemblyPublicKeyToken
{
get { return _assemblyPublicKeyToken; }
set { _assemblyPublicKeyToken = value; }
}
///<summary>Array of loose files associated with this assembly</summary>
public string [] ContentList
{
get { return _contentList; }
set { _contentList = value; }
}
/// <summary>
/// Splash screen image to be displayed before application init
/// </summary>
public string SplashImage
{
set { _splashImage = value; }
}
///<summary> Array of local xaml page files to be compiled at pass2 </summary>
public string[] LocalXamlPages
{
get { return _localXamlPages; }
}
///<summary> Local Application xaml file to be compiled at pass2 </summary>
public string LocalXamlApplication
{
get { return _localXamlApplication; }
}
/// <summary>
/// ReferenceAssemblyList. every item is an instance of ReferenceAssembly.
/// </summary>
/// <value></value>
public ArrayList ReferenceAssemblyList
{
get { return _referenceAssemblyList; }
set { _referenceAssemblyList = value; }
}
///<summary>The language source file extension set in a project or the registered default.</summary>
public string LanguageSourceExtension
{
get { return _languageSourceExtension; }
set { _languageSourceExtension = value; }
}
///<summary>Allows to hook a custom parser during compilation.</summary>
public ParserHooks ParserHooks
{
get { return _parserHooks; }
set { _parserHooks = value; }
}
///<summary>If true code for supporting hosting in Browser is generated</summary>
public bool HostInBrowser
{
get { return _hostInBrowser; }
set { _hostInBrowser = value; }
}
///<summary>Generate Debug information in the BAML file.</summary>
public bool XamlDebuggingInformation
{
get { return _xamlDebuggingInformation; }
set { _xamlDebuggingInformation = value; }
}
///<summary>Gets or sets the checksum algorithm used in code-behind.</summary>
public string ChecksumAlgorithm { get; set; }
/// <summary>
/// Get/Sets the TaskFileService which is used for abstracting simple
/// files services provided by CLR and the HostObject (IVsMsBuildTaskFileManager)
/// </summary>
internal ITaskFileService TaskFileService
{
get { return _taskFileService; }
set { _taskFileService = value; }
}
/// <summary>
/// The CompilerWrapper's TaskLoggingHelper for reporting compiler warnings.
/// </summary>
internal TaskLoggingHelper TaskLogger
{
set { _taskLogger = value; }
}
/// <summary>
/// Support custom IntermediateOutputPath and BaseIntermediateOutputPath outside the project path
/// </summary>
internal bool SupportCustomOutputPaths { get; set; } = false;
// If the xaml has local references, then it could have internal element & properties
// but there is no way to determine this until MCPass2. Yet, GeneratedInternalTypeHelper,
// which is the class that allows access to legitimate internals, needs to be generated
// in MCPass1. So, to determine if GeneratedInternalTypeHelper.cs needs to be kept or not,
// MCPass1 & MCPass2 task will use the HasInternals property as indicated below:
// In pass1, if this property returns true, it will be due to friend internals & so
// MCPass1 will just decide to keep the file & this property will be ignored in MCPass2
// if that task is executed as well.
// In pass1, if this property returns false, MCPass2 will look at this property again after
// the Xaml Compiler has been called to compile all the local markup files. Now if this
// property still returns false, the file will be removed, else it will be kept, because
// this property was true as the xaml compiler encountered local or friend internals
// during MCPass2.
// The above will apply only for a clean build, not incremental build.
public static bool HasInternals
{
get { return XamlTypeMapper.HasInternals; }
}
#endregion Public Properties
#region Public Events
/// <summary>
/// The Error event is fired when an error is encountered while compiling a xaml file.
/// </summary>
public event MarkupErrorEventHandler Error;
/// <summary>
/// The SourceFileResolve event is fired when it starts to compile one xaml file or handle
/// resource file. The event handler will resolve the original filepath to a new
/// SourcePath and RelativeSourceFilePath.
/// </summary>
public event SourceFileResolveEventHandler SourceFileResolve;
#endregion Public Events
#region Public Methods
///<summary>Complies list of file items comprising an Application.</summary>
public void Compile(CompilationUnit cu)
{
// KnownTypes, XamlTypeMapper, and ReflectionHelper all hold on to data statically that
// must not be reused between compilations as different compilations can target different
//
// Defensively clear static data even though the prior compilation should have done it.
// This is done to mitigate against a failed cleanup of a prior compilation from influencing
// the current compilation.
XamlTypeMapper.Clear();
KnownTypes.Clear();
ReflectionHelper.Dispose();
try
{
CompileCore(cu);
}
finally
{
// Don't rely on next compilation to reset as that would unnecessarily delay
// garbage collection of this static data that cannot be reused by the next compilation.
// Also, the ReflectionHelper must be disposed now to release file locks on assemblies
KnownTypes.Clear();
ReflectionHelper.Dispose();
}
}
private void CompileCore(CompilationUnit cu)
{
AssemblyName = cu.AssemblyName;
InitCompilerState();
DefaultNamespace = cu.DefaultNamespace;
_compilationUnitSourcePath = cu.SourcePath;
if (!IsLanguageSupported(cu.Language))
{
OnError(new Exception(SR.Format(SR.UnknownLanguage, cu.Language)));
return;
}
if (!cu.Pass2)
{
EnsureLanguageSourceExtension();
}
if (cu.ApplicationFile.Path != null && cu.ApplicationFile.Path.Length > 0)
{
Initialize(cu.ApplicationFile);
ApplicationFile = SourceFileInfo.RelativeSourceFilePath;
if (ApplicationFile.Length > 0)
{
IsCompilingEntryPointClass = true;
_Compile(cu.ApplicationFile.Path, cu.Pass2);
IsCompilingEntryPointClass = false;
if (_pendingLocalFiles != null && _pendingLocalFiles.Count == 1)
{
Debug.Assert(!cu.Pass2);
_localXamlApplication = (string)_pendingLocalFiles[0];
_pendingLocalFiles.Clear();
}
}
}
if (cu.FileList != null)
{
for (int i = 0; i < cu.FileList.Length; i++)
{
FileUnit sourceFile = cu.FileList[i];
Initialize(sourceFile);
if (SourceFileInfo.RelativeSourceFilePath.Length > 0)
{
_Compile(sourceFile.Path, cu.Pass2);
}
}
if (_pendingLocalFiles != null && _pendingLocalFiles.Count > 0)
{
Debug.Assert(!cu.Pass2);
_localXamlPages = (string[])_pendingLocalFiles.ToArray(typeof(string));
_pendingLocalFiles.Clear();
}
}
if (!cu.Pass2 && ContentList != null && ContentList.Length > 0)
{
GenerateLooseContentAttributes();
}
Debug.Assert(!cu.Pass2 || _pendingLocalFiles == null);
Debug.Assert(_pendingLocalFiles == null || _pendingLocalFiles.Count == 0);
_pendingLocalFiles = null;
if (cu.Pass2)
{
_localAssembly = null;
_localXamlApplication = null;
_localXamlPages = null;
}
}
#endregion Public Methods
#endregion ExternalAPI
#region Implementation
#region Properties
private CompilerInfo CompilerInfo
{
get { return _ci; }
set
{
_ci = value;
if (value == null)
{
_codeProvider = null;
}
}
}
private string ApplicationFile
{
get { return _applicationFile; }
set { _applicationFile = value; }
}
private string DefaultNamespace
{
get { return _defaultNamespace; }
set
{
IsValidCLRNamespace(value, true);
_defaultNamespace = value;
}
}
private bool IsCodeNeeded
{
get { return _isCodeNeeded; }
set { _isCodeNeeded = value; }
}
internal bool IsBamlNeeded
{
get { return IsCompilingEntryPointClass ? _isBamlNeeded : true; }
set { _isBamlNeeded = value; }
}
internal bool IsRootPublic
{
get { return _ccRoot != null && _ccRoot.CodeClass.TypeAttributes == TypeAttributes.Public; }
}
internal bool ProcessingRootContext
{
get { return _ccRoot == null; }
}
internal bool IsRootNameScope
{
get
{
CodeContext cc = (CodeContext)_codeContexts.Peek();
return cc.IsAllowedNameScope;
}
}
internal bool HasLocalEvent
{
get { return _hasLocalEvent; }
set { _hasLocalEvent = value; }
}
internal static bool HasLocalReference
{
get { return XamlTypeMapper.HasLocalReference; }
}
internal Assembly LocalAssembly
{
get
{
if (_localAssembly == null)
{
if (LocalAssemblyFile != null)
{
_localAssembly = ReflectionHelper.LoadAssembly(LocalAssemblyFile.AssemblyName, LocalAssemblyFile.Path);
}
}
return _localAssembly;
}
}
internal ReferenceAssembly LocalAssemblyFile
{
get { return _localAssemblyFile; }
set { _localAssemblyFile = value; }
}
internal string AssemblyName
{
get { return _assemblyName; }
set { _assemblyName = value; }
}
internal SourceFileInfo SourceFileInfo
{
get { return _sourceFileInfo; }
set { _sourceFileInfo = value; }
}
internal bool IsCompilingEntryPointClass
{
get { return _isCompilingEntryPointClass; }
set { _isCompilingEntryPointClass = value; _isBamlNeeded = !value; }
}
internal static string DefinitionNSPrefix
{
get { return _definitionNSPrefix; }
set { _definitionNSPrefix = value; }
}
internal string Language
{
get { return _language; }
}
#endregion Properties
#region CompileUnit
private void InitCompilerState()
{
_hasGeneratedInternalTypeHelper = false;
CompilerInfo = null;
InitializeReflectionHelper();
InitializeTypeMapper();
}
//
// Generate the SourceFileInfo for the source file.
// Do the appropriate initiallization work and file checking.
//
private void Initialize(FileUnit sourceFile)
{
try
{
// Keep the SourceFileInfo for the passed source file.
SourceFileInfo = OnSourceFileResolve(sourceFile);
// Process the input file
if (sourceFile.Path == null || !SourceFileInfo.IsXamlFile)
{
ThrowCompilerException(nameof(SR.InvalidMarkupFile));
}
if (!TaskFileService.Exists(sourceFile.Path))
{
ThrowCompilerException(nameof(SR.FileNotFound), sourceFile.Path);
}
// Prime the output directory
if (TargetPath.Length > 0)
{
// check for ending Path.DirectorySeparatorChar
if (!TargetPath.EndsWith(string.Empty + Path.DirectorySeparatorChar, StringComparison.Ordinal))
{
TargetPath += Path.DirectorySeparatorChar;
}
}
int pathEndIndex = SourceFileInfo.RelativeSourceFilePath.LastIndexOf(Path.DirectorySeparatorChar);
string targetPath = TargetPath + SourceFileInfo.RelativeSourceFilePath.Substring(0, pathEndIndex + 1);
// Create if not already exists
if (targetPath.Length > 0 && !Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
}
// All exceptions including NullRef & SEH need to be caught by the markupcompiler
// since it is an app and not a component.
#pragma warning suppress 6500
catch (Exception e)
{
OnError(e);
}
}
private void _Compile(string relativeSourceFile, bool pass2)
{
bool error = false;
Stream bamlStream = null;
XamlParser xamlParser = null;
try
{
DefinitionNSPrefix = DEFINITION_PREFIX;
IsCodeNeeded = false;
_ccRoot = null;
_hasLocalEvent = false;
_codeContexts = new Stack();
_parserContext = new ParserContext();
_parserContext.XamlTypeMapper = _typeMapper;
_hasEmittedEventSetterDeclaration = false;
bamlStream = new MemoryStream();
BamlRecordWriter bamlWriter = new BamlRecordWriter(bamlStream, _parserContext, true);
bamlWriter.DebugBamlStream = XamlDebuggingInformation;
xamlParser = new ParserExtension(this, _parserContext, bamlWriter, SourceFileInfo.Stream, pass2);
xamlParser.ParserHooks = ParserHooks;
try
{
xamlParser.Parse();
}
finally
{
_typeMapper.ResetMapper();
}
}
catch (XamlParseException e)
{
OnError(e);
error = true;
}
// All exceptions including NullRef & SEH need to be caught by the markupcompiler
// since it is an app and not a component.
#pragma warning suppress 6500
catch (Exception e)
{
OnError(e);
error = true;
}
finally
{
if (!error &&
xamlParser.BamlRecordWriter == null &&
IsBamlNeeded)
{
if (_pendingLocalFiles == null)
{
_pendingLocalFiles = new ArrayList(10);
}
_pendingLocalFiles.Add(relativeSourceFile);
}
if (_codeContexts != null)
{
_codeContexts.Clear();
_codeContexts = null;
}
if (SourceFileInfo != null)
{
SourceFileInfo.CloseStream();
}
if (bamlStream != null)
{
bamlStream.Close();
bamlStream = null;
}
}
}
private void GenerateSource()
{
Debug.Assert(_codeContexts.Count == 0);
CodeNamespace cnsImports = IsLanguageCSharp ? new CodeNamespace() : _ccRoot.CodeNS;
if (IsCodeNeeded)
{
cnsImports.Imports.Add(new CodeNamespaceImport("System"));
if (_usingNS != null)
{
foreach (string u in _usingNS)
{
cnsImports.Imports.Add(new CodeNamespaceImport(u));
}
}
// } end SubClass
_ccRoot.CodeNS.Types.Add(_ccRoot.CodeClass);
}
if (_usingNS != null)
{
_usingNS.Clear();
_usingNS = null;
}
if (IsCompilingEntryPointClass)
{
GenerateAppEntryPoint();
}
if (IsCodeNeeded)
{
MemoryStream codeMemStream = new MemoryStream();
// using Disposes the StreamWriter when it ends. Disposing the StreamWriter
// also closes the underlying MemoryStream. Furthermore, don't add BOM here since
// TaskFileService.WriteGeneratedCodeFileFile adds it.
using (StreamWriter codeStreamWriter = new StreamWriter(codeMemStream, new UTF8Encoding(false)))
{
CodeGeneratorOptions o = new CodeGeneratorOptions();
// } end namespace
CodeCompileUnit ccu = new CodeCompileUnit();
// generate pragma checksum data
Guid hashGuid = !string.IsNullOrEmpty(ChecksumAlgorithm) && ChecksumAlgorithm.Equals("SHA256", StringComparison.OrdinalIgnoreCase)
? s_hashSHA256Guid
: s_hashSHA1Guid;
CodeChecksumPragma csPragma = new CodeChecksumPragma();
csPragma.FileName = ParentFolderPrefix + SourceFileInfo.RelativeSourceFilePath + XAML;
csPragma.ChecksumAlgorithmId = hashGuid;
csPragma.ChecksumData = TaskFileService.GetChecksum(SourceFileInfo.OriginalFilePath, hashGuid);
ccu.StartDirectives.Add(csPragma);
if (cnsImports != _ccRoot.CodeNS)
{
ccu.Namespaces.Add(cnsImports);
}
ccu.Namespaces.Add(_ccRoot.CodeNS);
CodeDomProvider codeProvider = EnsureCodeProvider();
if (codeProvider.Supports(GeneratorSupport.PartialTypes) && _ccRoot.SubClass.Length == 0)
{
_ccRoot.CodeClass.IsPartial = true;
}
codeProvider.GenerateCodeFromCompileUnit(ccu, codeStreamWriter, o);
codeStreamWriter.Flush();
TaskFileService.WriteGeneratedCodeFile(codeMemStream.ToArray(),
TargetPath + SourceFileInfo.RelativeSourceFilePath,
SharedStrings.GeneratedExtension, SharedStrings.IntellisenseGeneratedExtension,
LanguageSourceExtension);
}
}
// Generate the InternalTypeHelper class in a separate code file only once and on an as
// needed basis for the current assembly being built. This class provides support for
// accessing legitimate internal types and properties that are present in the same (local)
// or a friend assembly and it is generated only when any such internals are actually
// encountered in any of the xaml files in the project.
GenerateInternalTypeHelperImplementation();
}
//
// Return FileInfo for the given source file.
//
private SourceFileInfo OnSourceFileResolve(FileUnit file)
{
SourceFileInfo sourceFileInfo;
if (SourceFileResolve != null)
{
//
// If SourceFileResolve event handler is registered, the handler
// is responsible for generating the SourceFileInfo.
// This is for MSBUILD tasks.
//
SourceFileResolveEventArgs scea = new SourceFileResolveEventArgs(file);
SourceFileResolve(this, scea);
sourceFileInfo = scea.SourceFileInfo;
}
else
{
// If SourceFileResolve event handler is not registered, generate
// the default SourceFileInfo for this file.
//
sourceFileInfo = new SourceFileInfo(file);
sourceFileInfo.SourcePath = _compilationUnitSourcePath;
if (sourceFileInfo.IsXamlFile)
{
int fileExtIndex = file.Path.LastIndexOf(DOTCHAR);
sourceFileInfo.RelativeSourceFilePath = file.Path.Substring(0, fileExtIndex);
}
}
return sourceFileInfo;
}
#endregion CompileUnit
#region ErrorHandling
static void ThrowCompilerException(string id)
{
string message = SR.GetResourceString(id);
ThrowCompilerExceptionImpl(message);
}
internal static void ThrowCompilerException(string id, string value)
{
string message = SR.Format(SR.GetResourceString(id), value);
ThrowCompilerExceptionImpl(message);
}
internal static void ThrowCompilerException(string id, string value1, string value2)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2);
ThrowCompilerExceptionImpl(message);
}
internal static void ThrowCompilerException(string id, string value1, string value2, string value3)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2, value3);
ThrowCompilerExceptionImpl(message);
}
static void ThrowCompilerException(string id, string value1, string value2, string value3, string value4)
{
string message = SR.Format(SR.GetResourceString(id), value1, value2, value3, value4);
ThrowCompilerExceptionImpl(message);
}
static void ThrowCompilerExceptionImpl(string message)
{
Exception compilerException = new Exception(message);
throw compilerException;
}
internal void OnError(Exception e)
{
// Don't treat an AssemblyVersion parsing error as a XamlParseException.
// Throw it back to the task execution.
if(e is AssemblyVersionParseException)
{
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(e).Throw();
}
if (Error != null)
{
XamlParseException xe = e as XamlParseException;
int lineNum = xe != null ? xe.LineNumber : 0;
int linePos = xe != null ? xe.LinePosition : 0;
string filename = SourceFileInfo.OriginalFilePath;
MarkupErrorEventArgs eea = new MarkupErrorEventArgs(e, lineNum, linePos, filename);
Error(this, eea);
}
}
#endregion ErrorHandling
#region Definition Namespace processing
internal void ProcessDefinitionNamespace(XamlDefTagNode xamlDefTagNode)
{
bool exitLoop = false;
XmlReader xmlReader = xamlDefTagNode.XmlReader;
string LocalName = xmlReader.LocalName;
bool isEmptyElement = xamlDefTagNode.IsEmptyElement;
bool isProcessingCodeTag = false;
do
{
XmlNodeType currNodeType = xmlReader.NodeType;
switch (currNodeType)
{
case XmlNodeType.Element:
{
if (isProcessingCodeTag)
{
ThrowCompilerException(nameof(SR.DefnTagsCannotBeNested), DefinitionNSPrefix, LocalName, xmlReader.LocalName);
}
switch (LocalName)
{
case CODETAG:
isProcessingCodeTag = true;
if (!IsCodeNeeded)
{
ThrowCompilerException(nameof(SR.MissingClassDefinitionForCodeTag),
_ccRoot.ElementName,
DefinitionNSPrefix,
SourceFileInfo.RelativeSourceFilePath + XAML);
}
bool moreAttributes = xmlReader.MoveToFirstAttribute();
while (moreAttributes)
{
string attributeNamespaceUri = xmlReader.LookupNamespace(xmlReader.Prefix);
if (!attributeNamespaceUri.Equals(XamlReaderHelper.DefinitionNamespaceURI) ||
!xmlReader.LocalName.Equals(XamlReaderHelper.DefinitionUid))
{
ThrowCompilerException(nameof(SR.AttributeNotAllowedOnCodeTag),
xmlReader.Name,
DefinitionNSPrefix,
CODETAG);
}
moreAttributes = xmlReader.MoveToNextAttribute();
}
break;
default:
ThrowCompilerException(nameof(SR.UnknownDefinitionTag), DefinitionNSPrefix, LocalName);
break;
}
// if an empty element do a Reader to
// get to the next node and then exit
if (isEmptyElement)
{
xmlReader.Read();
exitLoop = true;
}
break;
}
case XmlNodeType.EndElement:
{
xmlReader.Read();
exitLoop = true;
break;
}
case XmlNodeType.CDATA:
case XmlNodeType.Text:
{
IXmlLineInfo xmlLineInfo = xmlReader as IXmlLineInfo;
int lineNumber = 0;
if (null != xmlLineInfo)
{
lineNumber = xmlLineInfo.LineNumber;
}
if (LocalName.Equals(CODETAG))
{
AddCodeSnippet(xmlReader.Value, lineNumber);
}
else
{
ThrowCompilerException(nameof(SR.IllegalCDataTextScoping), DefinitionNSPrefix, LocalName, (currNodeType == XmlNodeType.CDATA ? "a CDATA section" : "text content"));
}
break;
}
}
}
while (!exitLoop && xmlReader.Read());
}
#endregion Definition Namespace processing
#region Baml Hookup Functions
private CodeMemberMethod EnsureStyleConnector()
{
if (_ccRoot.StyleConnectorFn == null)
{
_ccRoot.StyleConnectorFn = new CodeMemberMethod();
_ccRoot.StyleConnectorFn.Name = CONNECT;
_ccRoot.StyleConnectorFn.Attributes = MemberAttributes.Public | MemberAttributes.Final;
_ccRoot.StyleConnectorFn.PrivateImplementationType = new CodeTypeReference(KnownTypes.Types[(int)KnownElements.IStyleConnector]);
// void IStyleConnector.Connect(int connectionId, object target) {
//
CodeParameterDeclarationExpression param1 = new CodeParameterDeclarationExpression(typeof(int), CONNECTIONID);
CodeParameterDeclarationExpression param2 = new CodeParameterDeclarationExpression(typeof(object), TARGET);
_ccRoot.StyleConnectorFn.Parameters.Add(param1);
_ccRoot.StyleConnectorFn.Parameters.Add(param2);
AddDebuggerNonUserCodeAttribute(_ccRoot.StyleConnectorFn);
AddGeneratedCodeAttribute(_ccRoot.StyleConnectorFn);
AddEditorBrowsableAttribute(_ccRoot.StyleConnectorFn);
AddSuppressMessageAttribute(_ccRoot.StyleConnectorFn, "Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes");
AddSuppressMessageAttribute(_ccRoot.StyleConnectorFn, "Microsoft.Performance", "CA1800:DoNotCastUnnecessarily");
AddSuppressMessageAttribute(_ccRoot.StyleConnectorFn, "Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity");
if (SwitchStatementSupported())
{
// switch (connectionId) -- Start Switch
// {
CodeSnippetStatement css = new CodeSnippetStatement(SWITCH_STATEMENT);
_ccRoot.StyleConnectorFn.Statements.Add(css);
}
}
return _ccRoot.StyleConnectorFn;
}
internal void ConnectStyleEvent(XamlClrEventNode xamlClrEventNode)
{
CodeConditionStatement ccsConnector = null;
// validate the event handler name per C# grammar for identifiers
ValidateEventHandlerName(xamlClrEventNode.EventName, xamlClrEventNode.Value);
EnsureStyleConnector();
if (!xamlClrEventNode.IsSameScope)
{
int connectionId = xamlClrEventNode.ConnectionId;
if (SwitchStatementSupported())
{
// break any previous case staements as we are starting a new connection scope.
if (_ccRoot.StyleConnectorFn.Statements.Count > 1)
{
CodeSnippetStatement cssBreak = new CodeSnippetStatement(BREAK_STATEMENT);
_ccRoot.StyleConnectorFn.Statements.Add(cssBreak);
}
// case 1:
//
CodeSnippetStatement cssCase = new CodeSnippetStatement(CASE_STATEMENT + connectionId + COLON);
_ccRoot.StyleConnectorFn.Statements.Add(cssCase);
}
else
{
// if (connectionId == 1)
//
ccsConnector = new CodeConditionStatement();
ccsConnector.Condition = new CodeBinaryOperatorExpression(new CodeArgumentReferenceExpression(CONNECTIONID),
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression(connectionId));
}
}
else if (!SwitchStatementSupported())
{
// if in the same scope then use the if statement that was last generated
// at the start of the scope
Debug.Assert(_ccRoot.StyleConnectorFn.Statements.Count > 0);
ccsConnector = _ccRoot.StyleConnectorFn.Statements[_ccRoot.StyleConnectorFn.Statements.Count - 1] as CodeConditionStatement;
Debug.Assert(ccsConnector != null);
}
CodeArgumentReferenceExpression careTarget = new CodeArgumentReferenceExpression(TARGET);
if (xamlClrEventNode.IsStyleSetterEvent)
{
// EventSetter declaration only once to avoid warning!
if (!_hasEmittedEventSetterDeclaration)
{
_hasEmittedEventSetterDeclaration = true;
// EventSetter eventSetter;
//
CodeVariableDeclarationStatement cvdsES = new CodeVariableDeclarationStatement(KnownTypes.Types[(int)KnownElements.EventSetter], EVENTSETTER);
_ccRoot.StyleConnectorFn.Statements.Insert(0, cvdsES);
}
// eventSetter = new EventSetter();
//
CodeExpression[] esParams = {};
CodeVariableReferenceExpression cvreES = new CodeVariableReferenceExpression(EVENTSETTER);
CodeAssignStatement casES = new CodeAssignStatement(cvreES,
new CodeObjectCreateExpression(KnownTypes.Types[(int)KnownElements.EventSetter],
esParams));
// eventSetter.Event = Button.ClickEvent;
//
CodePropertyReferenceExpression cpreEvent = new CodePropertyReferenceExpression(cvreES, EVENT);
CodeAssignStatement casEvent = new CodeAssignStatement(cpreEvent,
GetEvent(xamlClrEventNode.EventMember,
xamlClrEventNode.EventName,
xamlClrEventNode.Value));
// eventSetter.Handler = new RoutedEventHandler(OnClick);
//
CodePropertyReferenceExpression cpreHandler = new CodePropertyReferenceExpression(cvreES, HANDLER);
CodeAssignStatement casHandler = new CodeAssignStatement(cpreHandler,
GetEventDelegate(null,
xamlClrEventNode.EventMember,
xamlClrEventNode.EventName,
xamlClrEventNode.Value));
AddLinePragma(casHandler, xamlClrEventNode.LineNumber);
// ((Style)target).Setters.Add(eventSetter);
//
CodeCastExpression cceTarget = new CodeCastExpression(KnownTypes.Types[(int)KnownElements.Style], careTarget);
CodePropertyReferenceExpression cpreSetters = new CodePropertyReferenceExpression(cceTarget, SETTERS);
CodeMethodInvokeExpression cmieAdd = new CodeMethodInvokeExpression(cpreSetters, ADD, cvreES);
if (SwitchStatementSupported())
{
_ccRoot.StyleConnectorFn.Statements.Add(casES);
_ccRoot.StyleConnectorFn.Statements.Add(casEvent);
_ccRoot.StyleConnectorFn.Statements.Add(casHandler);
_ccRoot.StyleConnectorFn.Statements.Add(new CodeExpressionStatement(cmieAdd));
}
else
{
ccsConnector.TrueStatements.Add(casES);
ccsConnector.TrueStatements.Add(casEvent);
ccsConnector.TrueStatements.Add(casHandler);
ccsConnector.TrueStatements.Add(new CodeExpressionStatement(cmieAdd));
// Only add if statement at start of new scope
if (!xamlClrEventNode.IsSameScope)
{
_ccRoot.StyleConnectorFn.Statements.Add(ccsConnector);
}
}
}
else
{
//
// ((Foo)target).Bar += new BarEventHandler(OnBar);
//
// *or*
//
// ((Foo)target).AddHandler( Baz.BarEvent, new BarEventHandler(OnBar));
//
CodeCastExpression cceTarget;
Type eventTarget;
// Create the markup event information
MarkupEventInfo mei = new MarkupEventInfo( xamlClrEventNode.Value, // Event handler string
xamlClrEventNode.EventName, // Event name string
xamlClrEventNode.EventMember, // MemberInfo
xamlClrEventNode.LineNumber); // LineNumber
// Get the type that defines the event (e.g. typeof(Button) for Button.Clicked or typeof(Mouse) for Mouse.MouseMove)
eventTarget = xamlClrEventNode.ListenerType;
// Create the type cast expression "(Foo)target"
cceTarget = new CodeCastExpression( eventTarget, careTarget);
// Create the whole code statement (either in += form or in AddHandler form)
CodeStatement csAddCLREvent = AddCLREvent( eventTarget, null, cceTarget, mei );
if (SwitchStatementSupported())
{
_ccRoot.StyleConnectorFn.Statements.Add( csAddCLREvent );
}
else
{
ccsConnector.TrueStatements.Add( csAddCLREvent );
// Only add if statement at start of new scope
if (!xamlClrEventNode.IsSameScope)
{
_ccRoot.StyleConnectorFn.Statements.Add(ccsConnector);
}
}
}
}
private void EndStyleEventConnection()
{
if (_ccRoot.StyleConnectorFn != null)
{
_ccRoot.CodeClass.BaseTypes.Add(KnownTypes.Types[(int)KnownElements.IStyleConnector].FullName);
if (SwitchStatementSupported())
{
// break any last case staement as we are done with style event connections.
if (_ccRoot.StyleConnectorFn.Statements.Count > 1)
{
CodeSnippetStatement cssBreak = new CodeSnippetStatement(BREAK_STATEMENT);
_ccRoot.StyleConnectorFn.Statements.Add(cssBreak);
}
// switch (connectionId)
// {
// } -- End Switch
CodeSnippetStatement css = new CodeSnippetStatement(INDENT12 + ENDCURLY);
_ccRoot.StyleConnectorFn.Statements.Add(css);
}
_ccRoot.CodeClass.Members.Add(_ccRoot.StyleConnectorFn);
_ccRoot.StyleConnectorFn = null;
}
}
private void EnsureHookupFn()
{
// void IComponentConnector.Connect
//
if (_ccRoot.HookupFn == null)
{
_ccRoot.HookupFn = new CodeMemberMethod();
_ccRoot.HookupFn.Name = CONNECT;
_ccRoot.HookupFn.Attributes = MemberAttributes.Public | MemberAttributes.Final;
_ccRoot.HookupFn.PrivateImplementationType = new CodeTypeReference(KnownTypes.Types[(int)KnownElements.IComponentConnector]);
// void IComponentConnector.Connect(int connectionId, object target) {
//
CodeParameterDeclarationExpression param1 = new CodeParameterDeclarationExpression(typeof(int), CONNECTIONID);
CodeParameterDeclarationExpression param2 = new CodeParameterDeclarationExpression(typeof(object), TARGET);
_ccRoot.HookupFn.Parameters.Add(param1);
_ccRoot.HookupFn.Parameters.Add(param2);
AddDebuggerNonUserCodeAttribute(_ccRoot.HookupFn);
AddGeneratedCodeAttribute(_ccRoot.HookupFn);
AddEditorBrowsableAttribute(_ccRoot.HookupFn);
AddSuppressMessageAttribute(_ccRoot.HookupFn, "Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes");
AddSuppressMessageAttribute(_ccRoot.HookupFn, "Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity");
AddSuppressMessageAttribute(_ccRoot.HookupFn, "Microsoft.Performance", "CA1800:DoNotCastUnnecessarily");
if (SwitchStatementSupported())
{
// switch (connectionId) -- Start Switch
// {
CodeSnippetStatement css = new CodeSnippetStatement(SWITCH_STATEMENT);
_ccRoot.HookupFn.Statements.Add(css);
}
}
}
internal void ConnectNameAndEvents(string elementName, ArrayList events, int connectionId)
{
CodeContext cc = (CodeContext)_codeContexts.Peek();
bool isAllowedNameScope = cc.IsAllowedNameScope;
if (_codeContexts.Count > 1 && KnownTypes.Types[(int)KnownElements.INameScope].IsAssignableFrom(cc.ElementType))
{
cc.IsAllowedNameScope = false;
}
if ((elementName == null || !isAllowedNameScope) && (events == null || events.Count == 0))
{
return;
}
EnsureHookupFn();
CodeConditionStatement ccsConnector = null;
if (SwitchStatementSupported())
{
// case 1:
//
CodeSnippetStatement cssCase = new CodeSnippetStatement(CASE_STATEMENT + connectionId + COLON);
_ccRoot.HookupFn.Statements.Add(cssCase);
}
else
{
// if (connectionId == 1)
//
ccsConnector = new CodeConditionStatement();
ccsConnector.Condition = new CodeBinaryOperatorExpression(new CodeArgumentReferenceExpression(CONNECTIONID),
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression(connectionId));
}
// (System.Windows.Controls.Footype)target;
CodeArgumentReferenceExpression careTarget = new CodeArgumentReferenceExpression(TARGET);
CodeCastExpression cceTarget = new CodeCastExpression(cc.ElementTypeReference, careTarget);
CodeExpression ceEvent = cceTarget;
// Names in nested Name scopes not be hooked up via ICC.Connect() as no fields are generated in this case.
if (elementName != null && isAllowedNameScope)
{
// this.fooId = (System.Windows.Controls.Footype)target;
//
ceEvent = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), elementName);
CodeAssignStatement casName = new CodeAssignStatement(ceEvent, cceTarget);
if (SwitchStatementSupported())
{
_ccRoot.HookupFn.Statements.Add(casName);
}
else
{
ccsConnector.TrueStatements.Add(casName);
}
}
if (events != null)
{
foreach (MarkupEventInfo mei in events)
{
CodeStatement csEvent = AddCLREvent(cc, ceEvent, mei);
if (SwitchStatementSupported())
{
_ccRoot.HookupFn.Statements.Add(csEvent);
}
else
{
ccsConnector.TrueStatements.Add(csEvent);
}
}
}
// return;
//
if (SwitchStatementSupported())
{
_ccRoot.HookupFn.Statements.Add(new CodeMethodReturnStatement());
}
else
{
ccsConnector.TrueStatements.Add(new CodeMethodReturnStatement());
_ccRoot.HookupFn.Statements.Add(ccsConnector);
}
}
// called from ParserExtension.WriteEndAttributes at the end of an element
// that has x:TypeArguments, to clear the state used to support them.
internal void ClearGenericTypeArgs()
{
_typeArgsList = null;
}
private void EndHookups()
{
if (_ccRoot.HookupFn != null)
{
var iComponentConnector = new CodeTypeReference(KnownTypes.Types[(int)KnownElements.IComponentConnector]);
_ccRoot.CodeClass.BaseTypes.Add(iComponentConnector);
// Visual Basic requires InitializeComponent to explicitly implement IComponentConnector.InitializeComponent
// if the class implements the interface. GenerateInitializeComponent handles the cases where the class is not
// the entry point for any programming language.
if (IsLanguageVB && IsCompilingEntryPointClass)
{
_ccRoot.InitializeComponentFn.ImplementationTypes.Add(iComponentConnector);
}
if (SwitchStatementSupported())
{
// Don't generate an empty Switch block!
if (_ccRoot.HookupFn.Statements.Count == 1 &&
_ccRoot.HookupFn.Statements[0] is CodeSnippetStatement)
{
_ccRoot.HookupFn.Statements.Clear();
}
else
{
// switch (connectionId)
// {
// } -- End Switch
CodeSnippetStatement css = new CodeSnippetStatement(INDENT12 + ENDCURLY);
_ccRoot.HookupFn.Statements.Add(css);
}
}
// _contentLoaded = true;
//
CodeFieldReferenceExpression cfreContentLoaded = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), CONTENT_LOADED);
CodeAssignStatement casContentLoaded = new CodeAssignStatement(cfreContentLoaded, new CodePrimitiveExpression(true));
_ccRoot.HookupFn.Statements.Add(casContentLoaded);
_ccRoot.CodeClass.Members.Add(_ccRoot.HookupFn);
_ccRoot.HookupFn = null;
}
}
internal void GenerateBamlFile(MemoryStream bamlMemStream)
{
// write baml file only if we're not doing intellisense build
if ((IsBamlNeeded) && (TaskFileService.IsRealBuild))
{
string filepath = TargetPath + SourceFileInfo.RelativeSourceFilePath + BAML;
using (FileStream bamlFileStream = new FileStream(filepath, FileMode.Create, FileAccess.Write))
{
bamlMemStream.WriteTo(bamlFileStream);
}
}
}
#endregion Baml Hookup Functions
#region Helpers
private void InitializeReflectionHelper()
{
var paths = new List<string>(ReferenceAssemblyList?.Count ?? 0);
if (ReferenceAssemblyList != null && ReferenceAssemblyList.Count > 0)
{
for (int i = 0; i < ReferenceAssemblyList.Count; i++)
{
ReferenceAssembly refasm = ReferenceAssemblyList[i] as ReferenceAssembly;
if (refasm != null && refasm.Path.Length > 0)
{
paths.Add(refasm.Path);
}
}
}
ReflectionHelper.Initialize(paths);
}
private void InitializeTypeMapper()
{
_typeMapper = XamlTypeMapper.DefaultMapper;
ReflectionHelper.LocalAssemblyName = AssemblyName;
if (ReferenceAssemblyList != null && ReferenceAssemblyList.Count > 0)
{
for (int i = 0; i < ReferenceAssemblyList.Count; i++)
{
ReferenceAssembly refasm = ReferenceAssemblyList[i] as ReferenceAssembly;
if (refasm != null && refasm.Path.Length > 0)
{
_typeMapper.SetAssemblyPath(refasm.AssemblyName, refasm.Path);
}
}
}
string asmMissing = string.Empty;
if (XamlTypeMapper.AssemblyWB == null)
{
asmMissing = "WindowsBase";
}
if (XamlTypeMapper.AssemblyPC == null)
{
asmMissing += $"{(asmMissing.Length > 0 ? ", " : string.Empty)}PresentationCore";
}
if (XamlTypeMapper.AssemblyPF == null)
{
asmMissing += $"{(asmMissing.Length > 0 ? ", " : string.Empty)}PresentationFramework";
}
if (asmMissing.Length > 0)
{
string message = SR.Format(SR.WinFXAssemblyMissing, asmMissing);
ApplicationException aeAssemblyMissing = new ApplicationException(message);
throw aeAssemblyMissing;
}
KnownTypes.InitializeKnownTypes(XamlTypeMapper.AssemblyPF, XamlTypeMapper.AssemblyPC, XamlTypeMapper.AssemblyWB);
_typeMapper.InitializeReferenceXmlnsCache();
}
private bool SwitchStatementSupported()
{
return IsLanguageCSharp || (CompilerInfo != null && string.Equals(CompilerInfo.GetLanguages()[0], JSCRIPT, StringComparison.OrdinalIgnoreCase));
}
private bool IsInternalAccessSupported
{
get
{
return CompilerInfo == null || !string.Equals(CompilerInfo.GetLanguages()[0], JSHARP, StringComparison.OrdinalIgnoreCase);
}
}
private bool IsLanguageCaseSensitive()
{
CodeDomProvider cdp = EnsureCodeProvider();
return cdp.LanguageOptions != LanguageOptions.CaseInsensitive;
}
private bool IsLanguageCSharp
{
get { return _isLangCSharp; }
}
private bool IsLanguageVB
{
get { return _isLangVB; }
}
// Combine namespace and className
private string GetFullClassName(string ns, string className)
{
string fullClass = className;
if (ns != null && ns.Length > 0)
{
fullClass = ns + DOT + className;
}
return fullClass;
}
internal void ValidateFullSubClassName(ref string subClassFullName)
{
bool isValid = false;
int index = subClassFullName.LastIndexOf(DOTCHAR);
if (index > 0)
{
string subClassName = subClassFullName.Substring(index + 1);
isValid = IsValidCLRNamespace(subClassFullName.Substring(0, index), false) &&
IsValidClassName(subClassName);
}
else
{
isValid = IsValidClassName(subClassFullName);
}
if (!isValid)
{
// flag error. Can't throw here as we are pre-scanning and parser context doesn't
// have customized linenum\linepos yet.
subClassFullName = DOT;
}
}
private bool CrackClassName(ref string className, out string ns)
{
bool isValid = true;
ns = string.Empty;
if (className.Length > 0)
{
// Split the Namespace
int index = className.LastIndexOf(DOTCHAR);
if (index > 0)
{
ns = className.Substring(0, index);
className = className.Substring(index + 1);
isValid = IsValidCLRNamespace(ns, false);
}
isValid = isValid && IsValidClassName(className);
}
return isValid;
}
internal string GetGenericTypeName(string typeName, string typeArgs)
{
if (typeArgs.Length == 0)
{
ThrowCompilerException(nameof(SR.UnknownGenericType),
DefinitionNSPrefix,
typeArgs,
typeName);
}
StringBuilder sb = new StringBuilder(typeName, 20);
sb.Append(GENERIC_DELIMITER);
_typeArgsList = typeArgs.Split(COMMA);
sb.Append(_typeArgsList.Length);
return sb.ToString();
}
private static void AddEditorBrowsableAttribute(CodeTypeMember ctmTarget)
{
CodeFieldReferenceExpression cfre = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EditorBrowsableState)), "Never");
CodeAttributeArgument caa = new CodeAttributeArgument(cfre);
ctmTarget.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(EditorBrowsableAttribute).FullName, caa));
}
private static void AddSuppressMessageAttribute(CodeTypeMember ctmTarget, string category, string rule)
{
CodeAttributeDeclaration cad = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(SuppressMessageAttribute)),
new CodeAttributeArgument(new CodePrimitiveExpression(category)),
new CodeAttributeArgument(new CodePrimitiveExpression(rule)));
ctmTarget.CustomAttributes.Add(cad);
}
private static void AddDebuggerNonUserCodeAttribute(CodeTypeMember ctmTarget)
{
CodeAttributeDeclaration cad = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(DebuggerNonUserCodeAttribute)));
ctmTarget.CustomAttributes.Add(cad);
}
internal static void GenerateXmlComments(CodeTypeMember ctm, string comment)
{
// generate xml comments
// /// <summary>
// /// </summary>
CodeCommentStatement ccs = new CodeCommentStatement(SummaryStartTag, true);
ctm.Comments.Add(ccs);
ccs = new CodeCommentStatement(comment, true);
ctm.Comments.Add(ccs);
ccs = new CodeCommentStatement(SummaryEndTag, true);
ctm.Comments.Add(ccs);
}
private bool IsValidClassName(string className)
{
if (className.Length == 0 ||!NameValidationHelper.IsValidIdentifierName(className))
{
return false;
}
return true;
}
private bool IsValidCLRNamespace(string ns, bool shouldThrow)
{
if (ns.Length > 0)
{
string[] nsParts = ns.Split(DOTCHAR);
foreach (string nsPart in nsParts)
{
if (!NameValidationHelper.IsValidIdentifierName(nsPart.Trim()))
{
if (shouldThrow)
{
ThrowCompilerException(nameof(SR.InvalidDefaultCLRNamespace), nsPart, ns);
}
else
{
return false;
}
}
}
}
return true;
}
internal void ValidateEventHandlerName(string eventName, string handlerName)
{
if (!IsCodeNeeded)
{
ThrowCompilerException(nameof(SR.MissingClassDefinitionForEvent), _ccRoot.ElementName, DefinitionNSPrefix, eventName);
}
string handler = handlerName.Trim();
if (handler.Length == 0)
{
ThrowCompilerException(nameof(SR.EmptyEventStringNotAllowed), eventName, handlerName);
}
else if (!NameValidationHelper.IsValidIdentifierName(handler))
{
ThrowCompilerException(nameof(SR.InvalidEventHandlerName), eventName, handlerName);
}
}
private string ParentFolderPrefix
{
get
{
if (SupportCustomOutputPaths)
{
// During code generation, ParentFolderPrefix returns the relative path from a .g.cs file to its markup file.
//
// One example is generated #pragmas: #pragma checksum "..\..\..\..\Views\ExportNotificationView.xaml"
//
// The path information for a markup file is represented in SourceFileInfo:
//
// SourceFileInfo.OriginalFilePath: "c:\\greenshot\\src\\Greenshot.Addons\\Views\\ExportNotificationView.xaml"
// SourceFileInfo.TargetPath: "c:\\greenshot\\src\\Greenshot.Addons\\obj\\Debug\\net6.0-windows\\"
// SourceFileInfo.RelativeFilePath: "Views\\ExportNotificationView"
// SourceFileInfo.SourcePath = "c:\\greenshot\\src\\Greenshot.Addons\\"
//
// The path of the generated code file associated with this markup file is:
//
// "c:\greenshot\src\Greenshot.Addons\obj\Debug\net6.0-windows\Views\ExportNotificationView.g.cs"
//
// The markup file path is in SourceFileInfo.OriginalFilePath:
//
// "c:\\greenshot\\src\\Greenshot.Addons\\Views\\ExportNotificationView.xaml"
//
// The relative path calculation must take in to account both the TargetPath and the RelativeFilePath:
//
// "c:\\greenshot\\src\\Greenshot.Addons\\obj\\Debug\\net6.0-windows\\" [SourceFileInfo.TargetPath]
// "Views\\ExportNotificationView" [SourceFileInfo.RelativeTargetPath]
//
// TargetPath concatenated with the directory portion of the RelativeTargetPath is the location to the .g.cs file:
//
// "c:\\greenshot\\src\\Greenshot.Addons\\obj\\Debug\\net6.0-windows\\Views"
//
string pathOfRelativeSourceFilePath = System.IO.Path.GetDirectoryName(SourceFileInfo.RelativeSourceFilePath);
// Return the parent folder of the target file with a trailing DirectorySeparatorChar.
// Return a relative path if possible. Else, return an absolute path.
#if NETFX
string path = PathInternal.GetRelativePath(TargetPath + pathOfRelativeSourceFilePath, SourceFileInfo.SourcePath, StringComparison.OrdinalIgnoreCase);
#else
string path = Path.GetRelativePath(TargetPath + pathOfRelativeSourceFilePath, SourceFileInfo.SourcePath);
#endif
// Always return a path with a trailing DirectorySeparatorChar.
return path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
}
else
{
string parentFolderPrefix = string.Empty;
if (TargetPath.StartsWith(SourceFileInfo.SourcePath, StringComparison.OrdinalIgnoreCase))
{
string relPath = TargetPath.Substring(SourceFileInfo.SourcePath.Length);
relPath += SourceFileInfo.RelativeSourceFilePath;
string[] dirs = relPath.Split(Path.DirectorySeparatorChar);
for (int i = 1; i < dirs.Length; i++)
{
parentFolderPrefix += PARENTFOLDER;
}
}
return parentFolderPrefix;
}
}
}
private void AddLinePragma(CodeTypeMember ctm, int lineNumber)
{
CodeLinePragma clp = new CodeLinePragma(ParentFolderPrefix + SourceFileInfo.RelativeSourceFilePath + XAML, lineNumber);
ctm.LinePragma = clp;
}
private void AddLinePragma(CodeStatement cs, int lineNumber)
{
CodeLinePragma clp = new CodeLinePragma(ParentFolderPrefix + SourceFileInfo.RelativeSourceFilePath + XAML, lineNumber);
cs.LinePragma = clp;
}
internal MemberAttributes GetMemberAttributes(string modifier)
{
if (!IsCodeNeeded)
{
ThrowCompilerException(nameof(SR.MissingClassWithFieldModifier), DefinitionNSPrefix);
}
if (_private.Length == 0)
{
bool converted = false;
CodeDomProvider codeProvider = EnsureCodeProvider();
TypeConverter converter = codeProvider.GetConverter(typeof(MemberAttributes));
if (converter != null)
{
if (converter.CanConvertTo(typeof(string)))
{
try
{
_private = converter.ConvertToInvariantString(MemberAttributes.Private).ToLowerInvariant();
_public = converter.ConvertToInvariantString(MemberAttributes.Public).ToLowerInvariant();
_protected = converter.ConvertToInvariantString(MemberAttributes.Family).ToLowerInvariant();
_internal = converter.ConvertToInvariantString(MemberAttributes.Assembly).ToLowerInvariant();
_protectedInternal = converter.ConvertToInvariantString(MemberAttributes.FamilyOrAssembly).ToLowerInvariant();
converted = true;
}
catch (NotSupportedException)
{
}
}
}
if (!converted)
{
ThrowCompilerException(nameof(SR.UnknownFieldModifier), MarkupCompiler.DefinitionNSPrefix, modifier, _language);
}
}
string normalizedModifier = modifier;
if (!IsLanguageCaseSensitive())
{
normalizedModifier = modifier.ToLowerInvariant();
}
if (normalizedModifier.Equals(_private))
{
return MemberAttributes.Private;
}
else if (normalizedModifier.Equals(_public))
{
return MemberAttributes.Public;
}
else if (normalizedModifier.Equals(_protected))
{
return MemberAttributes.Family;
}
else if (normalizedModifier.Equals(_internal))
{
return MemberAttributes.Assembly;
}
else if (normalizedModifier.Equals(_protectedInternal))
{
return MemberAttributes.FamilyOrAssembly;
}
else
{
ThrowCompilerException(nameof(SR.UnknownFieldModifier), MarkupCompiler.DefinitionNSPrefix, modifier, _language);
}
return MemberAttributes.Assembly;
}
private TypeAttributes GetTypeAttributes(ref string modifier)
{
if (modifier.Length > 0)
{
if (_privateClass.Length == 0)
{
bool converted = false;
CodeDomProvider codeProvider = EnsureCodeProvider();
TypeConverter converter = codeProvider.GetConverter(typeof(TypeAttributes));
if (converter != null)
{
if (converter.CanConvertTo(typeof(string)))
{
try
{
_privateClass = converter.ConvertToInvariantString(TypeAttributes.NotPublic).ToLowerInvariant();
_publicClass = converter.ConvertToInvariantString(TypeAttributes.Public).ToLowerInvariant();
converted = true;
}
catch (NotSupportedException)
{
}
}
}
if (!converted)
{
ThrowCompilerException(nameof(SR.UnknownClassModifier), MarkupCompiler.DefinitionNSPrefix, modifier, _language);
}
}
string normalizedModifier = modifier;
if (!IsLanguageCaseSensitive())
{
normalizedModifier = modifier.ToLowerInvariant();
}
if (normalizedModifier.Equals(_privateClass))
{
return TypeAttributes.NotPublic;
}
else if (normalizedModifier.Equals(_publicClass))
{
return TypeAttributes.Public;
}
else
{
// flag error. Can't throw here as we are pre-scanning and parser context doesn't
// have customized linenum\linepos yet.
modifier = DOT;
}
}
return TypeAttributes.Public;
}
internal void CheckForNestedNameScope()
{
CodeContext cc = (CodeContext)_codeContexts.Peek();
if (_codeContexts.Count > 1 && KnownTypes.Types[(int)KnownElements.INameScope].IsAssignableFrom(cc.ElementType))
{
cc.IsAllowedNameScope = false;
}
}
#endregion Helpers
#region Property
private CodeExpression GetPropertyValueExpression(ITypeDescriptorContext ctx, Type typeToConvertTo, Object value, string attributeValue)
{
CodeExpression ce = null;
InstanceDescriptor desc = null;
TypeConverter converter = null;
if (value != null && (typeToConvertTo == typeof(String) || typeToConvertTo.IsPrimitive))
{
ce = new CodePrimitiveExpression(value);
}
else if (typeToConvertTo == typeof(Uri))
{
converter = new UriTypeConverter();
if (!UriParser.IsKnownScheme(URISCHEME_PACK))
{
UriParser.Register(new GenericUriParser(GenericUriParserOptions.GenericAuthority), URISCHEME_PACK, -1);
}
}
else if (typeToConvertTo.IsEnum)
{
converter = new EnumConverter(typeToConvertTo);
}
if (converter != null)
{
if (value == null)
{
if (attributeValue != null)
{
value = converter.ConvertFromString(ctx, TypeConverterHelper.InvariantEnglishUS, attributeValue);
if (value == null)
{
return null;
}
}
else
{
ce = new CodePrimitiveExpression(null);
return ce;
}
}
if (converter.CanConvertTo(ctx, typeof(InstanceDescriptor)))
{
desc = (InstanceDescriptor)converter.ConvertTo(ctx, TypeConverterHelper.InvariantEnglishUS, value, typeof(InstanceDescriptor));
Debug.Assert(desc != null);
// static field ref...
if (desc.MemberInfo is FieldInfo || desc.MemberInfo is PropertyInfo)
{
CodeFieldReferenceExpression cfre = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(desc.MemberInfo.DeclaringType.FullName), desc.MemberInfo.Name);
ce = cfre;
}
else // static method invoke
{
object[] args = new object[desc.Arguments.Count];
desc.Arguments.CopyTo(args, 0);
CodeExpression[] expressions = new CodeExpression[args.Length];
if (desc.MemberInfo is MethodInfo)
{
MethodInfo mi = (MethodInfo)desc.MemberInfo;
ParameterInfo[] parameters = mi.GetParameters();
for (int i = 0; i < args.Length; i++)
{
expressions[i] = GetPropertyValueExpression(ctx, parameters[i].ParameterType, args[i], null);
}
CodeMethodInvokeExpression cmie = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(desc.MemberInfo.DeclaringType.FullName), desc.MemberInfo.Name);
foreach (CodeExpression e in expressions)
{
cmie.Parameters.Add(e);
}
ce = cmie;
}
else if (desc.MemberInfo is ConstructorInfo) // instance ctor invoke
{
ConstructorInfo ci = (ConstructorInfo)desc.MemberInfo;
ParameterInfo[] parameters = ci.GetParameters();
for (int i = 0; i < args.Length; i++)
{
expressions[i] = GetPropertyValueExpression(ctx, parameters[i].ParameterType, args[i], null);
}
CodeObjectCreateExpression coce = new CodeObjectCreateExpression(desc.MemberInfo.DeclaringType.FullName);
foreach (CodeExpression e in expressions)
{
coce.Parameters.Add(e);
}
ce = coce;
}
}
}
}
return ce;
}
#endregion Property
#region Event
// The given MemberInfo could either be an EventInfo for a Clr event or a
// MethodInfo for a static Add{EventName}Handler helper for an attached event
private Type GetEventHandlerType(MemberInfo memberInfo)
{
Type eventHandlerType = null;
if (memberInfo is EventInfo)
{
EventInfo ei = (EventInfo)memberInfo;
eventHandlerType = ei.EventHandlerType;
}
else
{
MethodInfo mi = (MethodInfo)memberInfo;
ParameterInfo[] pis = mi.GetParameters();
Debug.Assert(pis != null && pis.Length == 2 && KnownTypes.Types[(int)KnownElements.DependencyObject].IsAssignableFrom(pis[0].ParameterType));
eventHandlerType = pis[1].ParameterType;
}
return eventHandlerType;
}
private CodeFieldReferenceExpression GetEvent(MemberInfo miEvent, string eventName, string eventHandler)
{
FieldInfo fiEvent = miEvent.DeclaringType.GetField(eventName + EVENT, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (fiEvent == null || fiEvent.FieldType != KnownTypes.Types[(int)KnownElements.RoutedEvent])
{
ThrowCompilerException(nameof(SR.RoutedEventNotRegistered), miEvent.DeclaringType.FullName, eventName, eventHandler);
}
CodeTypeReferenceExpression ctreEvent = new CodeTypeReferenceExpression(miEvent.DeclaringType.FullName);
CodeFieldReferenceExpression cfreEvent = new CodeFieldReferenceExpression(ctreEvent, fiEvent.Name);
return cfreEvent;
}
private CodeExpression GetEventDelegate(CodeContext cc, MemberInfo miEvent, string eventName, string eventHandler)
{
Type eventTarget = cc != null ? cc.ElementType : miEvent.DeclaringType;
string eventTargetName = eventTarget != null ? eventTarget.FullName : cc.LocalElementFullName;
bool subClassed = _ccRoot.SubClass.Length > 0;
CodeDelegateCreateExpression cdce = new CodeDelegateCreateExpression();
// Fetch the EventHandlerType from either the EventInfo or the MethodInfo
// for the Add{Propertyname}Handler method's MethodInfo
Type eventHandlerType = GetEventHandlerType(miEvent);
string [] typeArgsList = cc != null ? cc.GenericTypeArgs : null;
cdce.DelegateType = GenerateConstructedTypeReference(eventHandlerType, typeArgsList, eventTarget, eventTargetName, eventName);
cdce.MethodName = eventHandler.Trim() + (subClassed ? HELPER : string.Empty);
cdce.TargetObject = new CodeThisReferenceExpression();
CodeExpression cDelExp = cdce;
// NOTE: workaround for VB CodeDom bug which does not produce correct Delegate expression code
if (IsLanguageVB)
{
CodeExpression[] delParams = { cdce };
CodeObjectCreateExpression coce = new CodeObjectCreateExpression(eventHandlerType, delParams);
cDelExp = coce;
}
// The bug that this chunk of code works around was fixed but
// exposes a different bug. To work around the second bug, we
// remove the workaround for the first one.
// Note that the initial bug was not fixed for VB, so the code block above remains.
// else if (Language == CompilerLanguage.JScript)
// {
// CodeCastExpression cce = new CodeCastExpression(mei.ei.EventHandlerType, cdce);
// cDelExp = cce;
// }
return cDelExp;
}
private CodeStatement AddCLREvent(CodeContext cc, CodeExpression ce, MarkupEventInfo mei)
{
// Infer the event target's (aka the listener) type from the current code context
return AddCLREvent( cc.ElementType, cc, ce, mei );
}
private CodeStatement AddCLREvent(Type eventTarget, CodeContext cc, CodeExpression ce, MarkupEventInfo mei)
{
bool subClassed = _ccRoot.SubClass.Length > 0;
CodeStatement csEvent = null;
// param2: <FooEventHandler>
CodeExpression cDelExp = GetEventDelegate(cc, mei.mi, mei.eventName, mei.eventHandler);
if (mei.mi.DeclaringType.IsAssignableFrom(eventTarget))
{
// _element.FooEvent += new FooEventHandlerDelegate(OnFoo);
csEvent = new CodeAttachEventStatement(ce, mei.eventName, cDelExp);
}
else if (eventTarget == null || // for known attached events on unknown local tags
KnownTypes.Types[(int)KnownElements.UIElement].IsAssignableFrom(eventTarget) ||
KnownTypes.Types[(int)KnownElements.ContentElement].IsAssignableFrom(eventTarget))
{
// _element.AddHandler(FooEvent, new FooEventHandlerDelegate(OnFoo));
CodeFieldReferenceExpression cfreEvent = GetEvent(mei.mi, mei.eventName, mei.eventHandler);
CodeMethodInvokeExpression cmieAddHandler = new CodeMethodInvokeExpression(ce, ADDHANDLER, cfreEvent, cDelExp);
csEvent = new CodeExpressionStatement(cmieAddHandler);
}
else
{
string eventTargetName = eventTarget != null ? eventTarget.FullName : cc.LocalElementFullName;
ThrowCompilerException(nameof(SR.UnknownEventAttribute), mei.eventName, mei.eventHandler, eventTargetName);
}
// When x:SubClass is used, event handlers can be specified in a code-behind file, under this sub class.
// But these handler methods need to be accessible from the intermediary generated sub class. So an empty
// internal virtual method with the same signature as the handler method is generated in this intermediary
// sub class (in the generated file):
//
// internal virtual void OnFooEvent(object sender, FooEventArgs ea)
// {
// }
//
// Since a delegate cannot take the address of a virtual function, a non-virtual helper function
// with the same signature as the above function & which calls the above function is also generated:
//
// private void OnFooEventHelper(object sender, FooEventArgs ea)
// {
// OnFooEvent(sender, ea);
// }
//
// All this is done only if x:Subclass is specified, since this means that this sub class would need to be
// defined in a code-behind file. This also means that inline events (in <x:Code>) will not be supported.
if (subClassed)
{
GenerateProtectedEventHandlerMethod(mei);
}
AddLinePragma(csEvent, mei.lineNumber);
return csEvent;
}
private void GenerateProtectedEventHandlerMethod(MarkupEventInfo mei)
{
Debug.Assert(_ccRoot != null && _ccRoot.SubClass.Length > 0);
// Fetch the EventHandlerType from either the EventInfo or the MethodInfo
// for the Add{Propertyname}Handler method's MethodInfo
Type eventHandlerType = GetEventHandlerType(mei.mi);
MethodInfo methodInvoke = eventHandlerType.GetMethod("Invoke");
ParameterInfo[] pars = methodInvoke.GetParameters();
CodeMemberMethod cmmEventHandler = new CodeMemberMethod();
CodeMemberMethod cmmEventHandlerHelper = new CodeMemberMethod();
AddDebuggerNonUserCodeAttribute(cmmEventHandlerHelper);
AddGeneratedCodeAttribute(cmmEventHandlerHelper);
cmmEventHandler.Attributes = MemberAttributes.Assembly | MemberAttributes.Overloaded;
cmmEventHandler.ReturnType = new CodeTypeReference(typeof(void));
cmmEventHandler.Name = mei.eventHandler.Trim();
CodeMethodInvokeExpression cmieOnEvent = new CodeMethodInvokeExpression(null, cmmEventHandler.Name);
for (int i = 0; i < pars.Length; i++)
{
CodeParameterDeclarationExpression param = new CodeParameterDeclarationExpression(pars[i].ParameterType, pars[i].Name);
cmmEventHandler.Parameters.Add(param);
cmmEventHandlerHelper.Parameters.Add(param);
cmieOnEvent.Parameters.Add(new CodeArgumentReferenceExpression(pars[i].Name));
}
//
// internal virtual void OnFooEvent(object sender, FooEventArgs ea)
// {
// }
//
_ccRoot.CodeClass.Members.Add(cmmEventHandler);
cmmEventHandlerHelper.Name = cmmEventHandler.Name + HELPER;
cmmEventHandlerHelper.ReturnType = new CodeTypeReference(typeof(void));
cmmEventHandlerHelper.Statements.Add(new CodeExpressionStatement(cmieOnEvent));
//
// private void OnFooEventHelper(object sender, FooEventArgs ea)
// {
// OnFooEvent(sender, ea);
// }
//
_ccRoot.CodeClass.Members.Add(cmmEventHandlerHelper);
}
internal struct MarkupEventInfo
{
internal MarkupEventInfo(string eh, string en, MemberInfo mi, int ln)
{
eventHandler = eh;
eventName = en;
this.mi = mi;
lineNumber = ln;
}
internal string eventHandler;
internal string eventName;
internal MemberInfo mi;
internal int lineNumber;
}
#endregion Event
#region Language
private CodeDomProvider EnsureCodeProvider()
{
if (_codeProvider == null)
{
Debug.Assert(CompilerInfo != null && CompilerInfo.IsCodeDomProviderTypeValid);
_codeProvider = CompilerInfo.CreateProvider();
}
return _codeProvider;
}
private bool IsLanguageSupported(string language)
{
_language = language;
_isLangCSharp = string.Equals(language, CSHARP, StringComparison.OrdinalIgnoreCase);
if (IsLanguageCSharp)
{
_codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
return true;
}
else
{
_isLangVB = string.Equals(language, VB, StringComparison.OrdinalIgnoreCase);
if (IsLanguageVB)
{
_codeProvider = new Microsoft.VisualBasic.VBCodeProvider();
return true;
}
}
if (CodeDomProvider.IsDefinedLanguage(language))
{
CompilerInfo = CodeDomProvider.GetCompilerInfo(language);
return (CompilerInfo != null);
}
return false;
}
private void EnsureLanguageSourceExtension()
{
// If empty string is passed, use the default language source extension.
if (String.IsNullOrEmpty(LanguageSourceExtension))
{
if (CompilerInfo != null)
{
string[] listExtensions = CompilerInfo.GetExtensions();
LanguageSourceExtension = listExtensions[0];
}
else if (IsLanguageCSharp)
{
LanguageSourceExtension = ".cs";
}
else if (IsLanguageVB)
{
LanguageSourceExtension = ".vb";
}
}
}
#endregion Language
#region CorePageGen
internal CodeMemberField AddNameField(string name, int lineNumber, int linePosition)
{
CodeMemberField cmField = NameField(name, lineNumber, linePosition);
if (cmField != null)
{
AddLinePragma(cmField, lineNumber);
}
return cmField;
}
internal CodeMemberField NameField(string name, int lineNumber, int linePosition)
{
// Warn for named ResourceDictionary items.
if (_codeContexts.Count >= 2)
{
Type resourceDictionary = KnownTypes.Types[(int)KnownElements.ResourceDictionary];
Type iNameScope = KnownTypes.Types[(int)KnownElements.INameScope];
object[] contexts = _codeContexts.ToArray();
for (int i = 1; i < contexts.Length; ++i)
{
Type t = ((CodeContext)contexts[i]).ElementType;
if (iNameScope.IsAssignableFrom(t))
{
break;
}
if (resourceDictionary.IsAssignableFrom(t))
{
_taskLogger.LogWarningFromResources(
null,
null,
null,
SourceFileInfo.OriginalFilePath,
lineNumber,
linePosition,
0,
0,
nameof(SR.NamedResDictItemWarning),
((CodeContext)_codeContexts.Peek()).ElementType.FullName,
name
);
break;
}
}
}
// Names in nested Name scopes should not have Name fields
CodeContext cc = (CodeContext)_codeContexts.Peek();
if (!cc.IsAllowedNameScope)
{
return null;
}
CodeMemberField field = new CodeMemberField();
field.Name = name;
field.Attributes = MemberAttributes.Assembly;
field.Type = cc.ElementTypeReference;
field.CustomAttributes.Add(
new CodeAttributeDeclaration(
new CodeTypeReference("System.Diagnostics.CodeAnalysis.SuppressMessageAttribute"),
new CodeAttributeArgument(new CodePrimitiveExpression("Microsoft.Performance")),
new CodeAttributeArgument(new CodePrimitiveExpression("CA1823:AvoidUnusedPrivateFields"))));
// Generate WithEvents ID fields in VB for objects supporting events
field.UserData["WithEvents"] = true;
_ccRoot.CodeClass.Members.Add(field);
return field;
}
private void AddCodeSnippet(string codeText, int lineNum)
{
if (codeText == null || codeText.Trim().Length == 0)
return;
CodeSnippetTypeMember snippet = new CodeSnippetTypeMember();
AddLinePragma(snippet, lineNum);
snippet.Text = codeText;
_ccRoot.CodeClass.Members.Add(snippet);
}
internal void AddGenericArguments(ParserContext parserContext, string typeArgs)
{
if (_typeArgsList != null)
{
string localTypeArgNamespace = string.Empty;
string localTypeArgClassName = string.Empty;
// for each generic param in this Type ...
for (int i = 0; i < _typeArgsList.Length; i++)
{
Type currTypeArg = parserContext.XamlTypeMapper.GetTypeArgsType(_typeArgsList[i].Trim(),
parserContext,
out localTypeArgClassName,
out localTypeArgNamespace);
if (currTypeArg == null)
{
bool error = false;
if (localTypeArgNamespace.Length == 0 && localTypeArgClassName.Length == 0)
{
error = true;
}
else
{
error = !IsValidClassName(localTypeArgClassName) ||
!IsValidCLRNamespace(localTypeArgNamespace, false);
}
if (error)
{
ThrowCompilerException(nameof(SR.InvalidTypeName),
MarkupCompiler.DefinitionNSPrefix,
typeArgs,
_typeArgsList[i].Trim(),
(i + 1).ToString(CultureInfo.CurrentCulture));
}
else
{
_typeArgsList[i] = GetFullClassName(localTypeArgNamespace, localTypeArgClassName);
}
}
else
{
_typeArgsList[i] = currTypeArg.FullName;
}
// construct the type args list for the base class type to be generated
_ccRoot.CodeClass.BaseTypes[0].TypeArguments.Add(new CodeTypeReference(_typeArgsList[i]));
}
}
}
private static CodeTypeReference GenerateConstructedTypeReference(Type t, string [] typeArgsList, string genericName)
{
CodeTypeReference ctrConstructedType = null;
// If the type has generic parameters, then need to add TypeArguments to the CodeTypeReference of this Type
if (genericName.Length > 0 || t.IsGenericType)
{
Debug.Assert(genericName.Length > 0 || t.IsGenericTypeDefinition);
if (t != null)
{
Debug.Assert(genericName.Length == 0 && typeArgsList != null);
// NOTE: Remove when CodeDom is fixed to understand mangled generic names.
genericName = t.FullName;
int bang = genericName.IndexOf(GENERIC_DELIMITER);
if (bang > 0)
{
genericName = genericName.Substring(0, bang);
}
#if DBG
Type[] typeParams = t.GetGenericArguments();
// TypeArgument count must match TypeParameter count on generic type.
Debug.Assert(typeArgsList != null && typeArgsList.Length == typeParams.Length);
// for each generic param in this Type ...
for (int i = 0; i < typeArgsList.Length; i++)
{
// Type params should always be unbound
Debug.Assert(typeParams[i].IsGenericParameter);
}
#endif
}
ctrConstructedType = new CodeTypeReference(genericName);
}
else
{
ctrConstructedType = new CodeTypeReference(t.FullName);
}
return ctrConstructedType;
}
private static CodeTypeReference GenerateConstructedTypeReference(Type t, string [] typeArgsList, Type refType, string refTypeFullName, string eventName)
{
CodeTypeReference ctrConstructedType = null;
// If the type has generic parameters, then need to add TypeArguments to the CodeTypeReference of this Type
if (t.IsGenericType)
{
Type[] refTypeParams = null;
CodeTypeReference ctrTypeArg = null;
Type[] typeParams = t.GetGenericArguments();
// NOTE: Remove when CodeDom is fixed to understand mangled generic names.
string genericName = t.Namespace + DOT + t.Name;
int bang = genericName.IndexOf(GENERIC_DELIMITER);
if (bang > 0)
{
genericName = genericName.Substring(0, bang);
}
ctrConstructedType = new CodeTypeReference(genericName);
// NOTE: For certain types like EventHandler delegate types, CodeDom seems
// to add bogus CodeTypeReferences as TypeArguments, so it needs to be cleared explicitly.
ctrConstructedType.TypeArguments.Clear();
// for each generic param in this Type ...
foreach (Type typeParam in typeParams)
{
// if the param is unbound
if (typeParam.IsGenericParameter)
{
// get the generic params of the containing\reference Type, only once
if (refTypeParams == null)
{
if (refType == null || !refType.IsGenericType || !refType.IsGenericTypeDefinition || typeArgsList == null)
{
ThrowCompilerException(nameof(SR.ContainingTagNotGeneric), eventName, ctrConstructedType.BaseType, refTypeFullName);
}
refTypeParams = refType.GetGenericArguments();
}
ctrTypeArg = null;
// for each reference generic param
for (int i = 0; i < refTypeParams.Length; i++)
{
// if it matches the current generic param of this Type
if (refTypeParams[i] == typeParam)
{
// The TypeArgumentList must have already been populated with full Type names &
// the TypeArgument count must match TypeParameter count on generic reference type.
Debug.Assert(typeArgsList != null && typeArgsList.Length == refTypeParams.Length);
// Find the Type argument from the list that is in the same position as generic Type param
string currTypeArg = typeArgsList[i];
// and create a CodeTypeReference from it
ctrTypeArg = new CodeTypeReference(currTypeArg);
break;
}
}
// no match!
if (ctrTypeArg == null)
{
ThrowCompilerException(nameof(SR.MatchingTypeArgsNotFoundInRefType),
eventName,
ctrConstructedType.BaseType,
typeParam.FullName,
$"{refTypeFullName}<{string.Join(",", typeArgsList)}>");
}
}
else
{
ctrTypeArg = new CodeTypeReference(typeParam);
}
// construct the type args list for the base class type to be generated
ctrConstructedType.TypeArguments.Add(ctrTypeArg);
}
}
else
{
ctrConstructedType = new CodeTypeReference(t.FullName);
}
return ctrConstructedType;
}
private static void AddGeneratedCodeAttribute(CodeTypeMember ctmTarget)
{
if (s_generatedCode_ToolName == null || s_generatedCode_ToolVersion == null)
{
AssemblyName assemblyName = new AssemblyName(Assembly.GetExecutingAssembly().FullName);
s_generatedCode_ToolName = assemblyName.Name;
s_generatedCode_ToolVersion = assemblyName.Version.ToString();
}
CodeAttributeDeclaration cad = new CodeAttributeDeclaration(
new CodeTypeReference(typeof(GeneratedCodeAttribute)),
new CodeAttributeArgument(new CodePrimitiveExpression(s_generatedCode_ToolName)),
new CodeAttributeArgument(new CodePrimitiveExpression(s_generatedCode_ToolVersion)));
ctmTarget.CustomAttributes.Add(cad);
}
private CodeTypeDeclaration GenerateClass(string className, ref string modifier, Type baseClass, string baseClassFullName)
{
// public class MyClass : BaseClass {
//
CodeTypeReference ctrBaseClass = null;
CodeTypeDeclaration ctdClass = new CodeTypeDeclaration();
ctdClass.Name = className;
if (baseClass != null)
{
// At this point, we should only have fully open generic types if there is a typeargs list.
Debug.Assert(_typeArgsList == null || (baseClass.IsGenericType && baseClass.IsGenericTypeDefinition));
Debug.Assert(_typeArgsList != null || !baseClass.IsGenericType);
ctrBaseClass = GenerateConstructedTypeReference(baseClass, _typeArgsList, string.Empty);
// Add the type reference for the normal or fully constructed generic base class
ctdClass.BaseTypes.Add(ctrBaseClass);
}
else if (baseClassFullName.Length > 0)
{
ctrBaseClass = GenerateConstructedTypeReference(null, _typeArgsList, baseClassFullName);
// Add the type reference for the local base class
ctdClass.BaseTypes.Add(ctrBaseClass);
}
ctdClass.TypeAttributes = GetTypeAttributes(ref modifier);
ctdClass.Members.Clear();
if (TypeAttributes.Public == ctdClass.TypeAttributes)
{
GenerateXmlComments(ctdClass, className);
}
// VBNOTE: The VB compiler will generate a ctor with an InitializeComponent call if not explicitly specified
// by the user if this Attribute is set on the class.
if (IsLanguageVB && !IsCompilingEntryPointClass)
{
ctdClass.CustomAttributes.Add(new CodeAttributeDeclaration("Microsoft.VisualBasic.CompilerServices.DesignerGenerated"));
}
return ctdClass;
}
private CodeContext GenerateSubClass(ref string className, ref string modifier, Type baseClass, string baseClassFullName)
{
string ns = string.Empty;
string baseClassName = string.Empty;
bool isValidClassName = CrackClassName(ref className, out ns);
if (!string.IsNullOrEmpty(baseClassFullName))
{
int dotIndex = baseClassFullName.LastIndexOf(DOTCHAR);
if (dotIndex != -1)
{
baseClassName = baseClassFullName.Substring(dotIndex + 1);
if (!IsValidClassName(baseClassName))
{
ThrowCompilerException(nameof(SR.InvalidBaseClassName), baseClassName);
}
string bns = baseClassFullName.Substring(0, dotIndex);
if (!IsValidCLRNamespace(bns, false))
{
ThrowCompilerException(nameof(SR.InvalidBaseClassNamespace), bns, baseClassName);
}
}
}
if (!isValidClassName)
{
// flag error. Can't throw here as we are pre-scanning and parser context doesn't
// have customized linenum\linepos yet.
className = DOT;
}
else if (IsCompilingEntryPointClass && className.Length == 0)
{
string baseName = baseClass != null ? baseClass.Name : baseClassName;
className = ANONYMOUS_ENTRYCLASS_PREFIX + baseName;
Debug.Assert(!string.IsNullOrEmpty(baseName));
ns = XamlTypeMapper.GeneratedNamespace;
}
// namespace MyNamespace
// {
Debug.Assert(_ccRoot == null);
Debug.Assert(_codeContexts == null || _codeContexts.Count == 0, "mismatched CodeContexts");
CodeNamespace cns = new CodeNamespace();
cns.Name = ns;
cns.Types.Clear();
CodeTypeDeclaration ctdClass = GenerateClass(className, ref modifier, baseClass, baseClassFullName);
CodeContext cc = new CodeContextRoot(ctdClass, cns, baseClass, _typeArgsList, baseClassFullName);
cc.ElementTypeReference = new CodeTypeReference(GetFullClassName(ns, className));
return cc;
}
private void GenerateCreateDelegateHelper()
{
if (!IsInternalAccessSupported || !HasLocalEvent)
{
return;
}
Debug.Assert(HasLocalReference, "if we have a local event, there should be a local mapping PI for it");
// internal Delegate _CreateDelegate(Type delegateType, string handler)
// {
// return Delegate.CreateDelegate(delegateType, this, handler);
// }
//
CodeMemberMethod cmmCD = new CodeMemberMethod();
cmmCD.Name = CREATEDELEGATEHELPER;
cmmCD.ReturnType = new CodeTypeReference(typeof(Delegate));
cmmCD.Attributes = MemberAttributes.Assembly | MemberAttributes.Final;
AddDebuggerNonUserCodeAttribute(cmmCD);
AddGeneratedCodeAttribute(cmmCD);
AddSuppressMessageAttribute(cmmCD, "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode");
CodeParameterDeclarationExpression param1 = new CodeParameterDeclarationExpression(typeof(Type), DELEGATETYPE);
CodeParameterDeclarationExpression param2 = new CodeParameterDeclarationExpression(typeof(string), HANDLERARG);
cmmCD.Parameters.Add(param1);
cmmCD.Parameters.Add(param2);
CodeMethodReferenceExpression cmreCD = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(Delegate)), "CreateDelegate");
CodeMethodInvokeExpression cmieCD = new CodeMethodInvokeExpression();
cmieCD.Method = cmreCD;
cmieCD.Parameters.Add(new CodeArgumentReferenceExpression(DELEGATETYPE));
cmieCD.Parameters.Add(new CodeThisReferenceExpression());
cmieCD.Parameters.Add(new CodeArgumentReferenceExpression(HANDLERARG));
cmmCD.Statements.Add(new CodeMethodReturnStatement(cmieCD));
_ccRoot.CodeClass.Members.Add(cmmCD);
}
private void GenerateInitializeComponent(bool isApp)
{
// public void InitializeComponent()
// {
//
CodeMemberMethod cmmLC = _ccRoot.InitializeComponentFn;
if (cmmLC == null)
{
cmmLC = _ccRoot.EnsureInitializeComponentFn;
if (!isApp)
{
cmmLC.ImplementationTypes.Add(new CodeTypeReference(KnownTypes.Types[(int)KnownElements.IComponentConnector]));
}
}
// if (_contentLoaded)
// {
// return;
// }
//
CodeConditionStatement ccsCL = new CodeConditionStatement();
ccsCL.Condition = new CodeFieldReferenceExpression(null, CONTENT_LOADED);
ccsCL.TrueStatements.Add(new CodeMethodReturnStatement());
if (!isApp)
{
cmmLC.Statements.Add(ccsCL);
}
else
{
cmmLC.Statements.Insert(0, ccsCL);
}
// _contentLoaded = true;
//
CodeAssignStatement casCL = new CodeAssignStatement(new CodeFieldReferenceExpression(null, CONTENT_LOADED),
new CodePrimitiveExpression(true));
if (!isApp)
{
cmmLC.Statements.Add(casCL);
}
else
{
cmmLC.Statements.Insert(1, casCL);
}
// Generate canonicalized string as resource id.
bool requestExtensionChange = false;
string resourceID = ResourcesGenerator.GetResourceIdForResourceFile(
SourceFileInfo.RelativeSourceFilePath + XAML,
SourceFileInfo.OriginalFileLinkAlias,
SourceFileInfo.OriginalFileLogicalName,
TargetPath,
Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar,
requestExtensionChange);
string uriPart = string.Empty;
// Attempt to parse out the AssemblyVersion if it exists. This validates that we can either use an empty version string (wildcards exist)
// or we can utilize the passed in string (valid parse).
if (!VersionHelper.TryParseAssemblyVersion(AssemblyVersion, allowWildcard: true, version: out _, out bool hasWildcard)
&& !string.IsNullOrWhiteSpace(AssemblyVersion))
{
throw new AssemblyVersionParseException(SR.Format(SR.InvalidAssemblyVersion, AssemblyVersion));
}
// In .NET Framework (non-SDK-style projects), the process to use a wildcard AssemblyVersion is to do the following:
// - Modify the AssemblyVersionAttribute to a wildcard string (e.g. "1.2.*")
// - Set Deterministic to false in the build
// During MarkupCompilation, the AssemblyVersion property would not be set and WPF would correctly generate a resource URI without a version.
// In .NET Core/5 (or .NET Framework SDK-style projects), the same process can be used if GenerateAssemblyVersionAttribute is set to false in
// the build. However, this isn't really the idiomatic way to set the version for an assembly. Instead, developers are more likely to use the
// AssemblyVersion build property. If a developer explicitly sets the AssemblyVersion build property to a wildcard version string, we would use
// that as part of the URI here. This results in an error in Version.Parse during InitializeComponent's call tree. Instead, do as we would have
// when the developer sets a wildcard version string via AssemblyVersionAttribute and use an empty string.
string version = hasWildcard || String.IsNullOrEmpty(AssemblyVersion)
? String.Empty
: COMPONENT_DELIMITER + VER + AssemblyVersion;
string token = String.IsNullOrEmpty(AssemblyPublicKeyToken)
? String.Empty
: COMPONENT_DELIMITER + AssemblyPublicKeyToken;
uriPart = FORWARDSLASH + AssemblyName + version + token + COMPONENT_DELIMITER + COMPONENT + FORWARDSLASH + resourceID;
//
// Uri resourceLocator = new Uri(uriPart, UriKind.Relative);
//
string resVarname = RESOURCE_LOCATER;
CodeFieldReferenceExpression cfreRelUri = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(System.UriKind)), "Relative");
CodeExpression[] uriParams = { new CodePrimitiveExpression(uriPart), cfreRelUri };
CodeObjectCreateExpression coceResourceLocator = new CodeObjectCreateExpression(typeof(System.Uri), uriParams);
CodeVariableDeclarationStatement cvdsresLocator = new CodeVariableDeclarationStatement(typeof(System.Uri), resVarname, coceResourceLocator);
cmmLC.Statements.Add(cvdsresLocator);
//
// System.Windows.Application.LoadComponent(this, resourceLocator);
//
CodeMethodReferenceExpression cmreLoadContent = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(KnownTypes.Types[(int)KnownElements.Application]), LOADCOMPONENT);
CodeMethodInvokeExpression cmieLoadContent = new CodeMethodInvokeExpression();
cmieLoadContent.Method = cmreLoadContent;
CodeVariableReferenceExpression cvreMemStm = new CodeVariableReferenceExpression(resVarname);
cmieLoadContent.Parameters.Add(new CodeThisReferenceExpression());
cmieLoadContent.Parameters.Add(cvreMemStm);
CodeExpressionStatement cesLC = new CodeExpressionStatement(cmieLoadContent);
AddLinePragma(cesLC, 1);
cmmLC.Statements.Add(cesLC);
// private bool _contentLoaded;
//
CodeMemberField cmfCL = new CodeMemberField();
cmfCL.Name = CONTENT_LOADED;
cmfCL.Attributes = MemberAttributes.Private;
cmfCL.Type = new CodeTypeReference(typeof(bool));
_ccRoot.CodeClass.Members.Add(cmfCL);
if (!isApp)
{
// Make sure that ICC.Connect is generated to avoid compilation errors
EnsureHookupFn();
}
}
private void GenerateInternalTypeHelperImplementation()
{
if (!IsInternalAccessSupported ||
!(HasInternals || HasLocalReference) ||
_hasGeneratedInternalTypeHelper)
{
return;
}
_hasGeneratedInternalTypeHelper = true;
// namespace XamlGeneratedNamespace
// {
//
CodeNamespace cns = new CodeNamespace();
cns.Name = XamlTypeMapper.GeneratedNamespace;
// [EditorBrowsable(EditorBrowsableState.Never)]
// public sealed class GeneratedInternalTypeHelper : InternalTypeHelper
// {
//
CodeTypeDeclaration ctdClass = new CodeTypeDeclaration();
ctdClass.Name = XamlTypeMapper.GeneratedInternalTypeHelperClassName;
ctdClass.BaseTypes.Add(new CodeTypeReference("System.Windows.Markup.InternalTypeHelper"));
ctdClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;
AddDebuggerNonUserCodeAttribute(ctdClass);
AddGeneratedCodeAttribute(ctdClass);
AddEditorBrowsableAttribute(ctdClass);
GenerateXmlComments(ctdClass, ctdClass.Name);
// protected override object CreateInstance(Type type, CultureInfo culture)
// {
// return Activator.CreateInstance(type,
// BindingFlags.Public |
// BindingFlags.NonPublic |
// BindingFlags.Instance |
// BindingFlags.CreateInstance,
// null,
// null,
// culture);
// }
//
CodeMemberMethod cmmCI = new CodeMemberMethod();
cmmCI.Name = "CreateInstance";
cmmCI.Attributes = MemberAttributes.Family | MemberAttributes.Override;
cmmCI.ReturnType = new CodeTypeReference(typeof(Object));
CodeParameterDeclarationExpression param1 = new CodeParameterDeclarationExpression(typeof(Type), TYPE);
CodeParameterDeclarationExpression param4 = new CodeParameterDeclarationExpression(typeof(CultureInfo), CULTURE);
cmmCI.Parameters.Add(param1);
cmmCI.Parameters.Add(param4);
CodeMethodReferenceExpression cmreCI = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(Activator)), "CreateInstance");
CodeMethodInvokeExpression cmieCI = new CodeMethodInvokeExpression();
cmieCI.Method = cmreCI;
cmieCI.Parameters.Add(new CodeArgumentReferenceExpression(TYPE));
CodeFieldReferenceExpression cfre1 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "Public");
CodeFieldReferenceExpression cfre2 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "NonPublic");
CodeFieldReferenceExpression cfre3 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "Instance");
CodeFieldReferenceExpression cfre4 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "CreateInstance");
CodeBinaryOperatorExpression cboe1 = new CodeBinaryOperatorExpression(cfre1, CodeBinaryOperatorType.BitwiseOr, cfre2);
CodeBinaryOperatorExpression cboe2 = new CodeBinaryOperatorExpression(cfre3, CodeBinaryOperatorType.BitwiseOr, cfre4);
CodeBinaryOperatorExpression cboeCI = new CodeBinaryOperatorExpression(cboe1, CodeBinaryOperatorType.BitwiseOr, cboe2);
cmieCI.Parameters.Add(cboeCI);
cmieCI.Parameters.Add(new CodePrimitiveExpression(null));
cmieCI.Parameters.Add(new CodePrimitiveExpression(null));
cmieCI.Parameters.Add(new CodeArgumentReferenceExpression(CULTURE));
cmmCI.Statements.Add(new CodeMethodReturnStatement(cmieCI));
GenerateXmlComments(cmmCI, cmmCI.Name);
ctdClass.Members.Add(cmmCI);
// protected override object GetPropertyValue(PropertyInfo propertyInfo, object target, CultureInfo culture)
// {
// return propertyInfo.GetValue(target, BindingFlags.Default, null, null, culture);
// }
//
CodeMemberMethod cmmGPV = new CodeMemberMethod();
cmmGPV.Name = "GetPropertyValue";
cmmGPV.Attributes = MemberAttributes.Family | MemberAttributes.Override;
cmmGPV.ReturnType = new CodeTypeReference(typeof(Object));
param1 = new CodeParameterDeclarationExpression(typeof(PropertyInfo), PROPINFO);
CodeParameterDeclarationExpression param2 = new CodeParameterDeclarationExpression(typeof(object), TARGET);
cmmGPV.Parameters.Add(param1);
cmmGPV.Parameters.Add(param2);
cmmGPV.Parameters.Add(param4);
CodeMethodReferenceExpression cmreGPV = new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression(PROPINFO), "GetValue");
CodeMethodInvokeExpression cmieGPV = new CodeMethodInvokeExpression();
cmieGPV.Method = cmreGPV;
cmieGPV.Parameters.Add(new CodeArgumentReferenceExpression(TARGET));
cmieGPV.Parameters.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), DEFAULT));
cmieGPV.Parameters.Add(new CodePrimitiveExpression(null));
cmieGPV.Parameters.Add(new CodePrimitiveExpression(null));
cmieGPV.Parameters.Add(new CodeArgumentReferenceExpression(CULTURE));
cmmGPV.Statements.Add(new CodeMethodReturnStatement(cmieGPV));
GenerateXmlComments(cmmGPV, cmmGPV.Name);
ctdClass.Members.Add(cmmGPV);
// protected override void SetPropertyValue(PropertyInfo propertyInfo, object target, object value, CultureInfo culture)
// {
// propertyInfo.SetValue(target, value, BindingFlags.Default, null, null, culture);
// }
//
CodeMemberMethod cmmSPV = new CodeMemberMethod();
cmmSPV.Name = "SetPropertyValue";
cmmSPV.Attributes = MemberAttributes.Family | MemberAttributes.Override;
CodeParameterDeclarationExpression param3 = new CodeParameterDeclarationExpression(typeof(object), VALUE);
cmmSPV.Parameters.Add(param1);
cmmSPV.Parameters.Add(param2);
cmmSPV.Parameters.Add(param3);
cmmSPV.Parameters.Add(param4);
CodeMethodReferenceExpression cmreSPV = new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression(PROPINFO), "SetValue");
CodeMethodInvokeExpression cmieSPV = new CodeMethodInvokeExpression();
cmieSPV.Method = cmreSPV;
cmieSPV.Parameters.Add(new CodeArgumentReferenceExpression(TARGET));
cmieSPV.Parameters.Add(new CodeArgumentReferenceExpression(VALUE));
cmieSPV.Parameters.Add(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), DEFAULT));
cmieSPV.Parameters.Add(new CodePrimitiveExpression(null));
cmieSPV.Parameters.Add(new CodePrimitiveExpression(null));
cmieSPV.Parameters.Add(new CodeArgumentReferenceExpression(CULTURE));
cmmSPV.Statements.Add(new CodeExpressionStatement(cmieSPV));
GenerateXmlComments(cmmSPV, cmmSPV.Name);
ctdClass.Members.Add(cmmSPV);
// protected override Delegate CreateDelegate(Type delegateType, object target, string handler)
// {
// return (Delegate)target.GetType().InvokeMember("_CreateDelegate",
// BindingFlags.Instance |
// BindingFlags.NonPublic |
// BindingFlags.InvokeMethod,
// null,
// target,
// new object[] { delegateType, handler });
// }
//
CodeMemberMethod cmmCD = new CodeMemberMethod();
cmmCD.Name = "CreateDelegate";
cmmCD.Attributes = MemberAttributes.Family | MemberAttributes.Override;
cmmCD.ReturnType = new CodeTypeReference(typeof(Delegate));
param1 = new CodeParameterDeclarationExpression(typeof(Type), DELEGATETYPE);
param3 = new CodeParameterDeclarationExpression(typeof(string), HANDLERARG);
cmmCD.Parameters.Add(param1);
cmmCD.Parameters.Add(param2);
cmmCD.Parameters.Add(param3);
CodeArgumentReferenceExpression careTarget = new CodeArgumentReferenceExpression(TARGET);
CodeMethodReferenceExpression cmreGetType = new CodeMethodReferenceExpression(careTarget, "GetType");
CodeMethodInvokeExpression cmieGetType = new CodeMethodInvokeExpression();
cmieGetType.Method = cmreGetType;
CodeMethodReferenceExpression cmreCD = new CodeMethodReferenceExpression(cmieGetType, "InvokeMember");
CodeMethodInvokeExpression cmieCD = new CodeMethodInvokeExpression();
cmieCD.Method = cmreCD;
cmieCD.Parameters.Add(new CodePrimitiveExpression(CREATEDELEGATEHELPER));
CodeFieldReferenceExpression cfre5 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "InvokeMethod");
CodeBinaryOperatorExpression cboe = new CodeBinaryOperatorExpression(cfre2, CodeBinaryOperatorType.BitwiseOr, cfre3);
CodeBinaryOperatorExpression cboeCD = new CodeBinaryOperatorExpression(cfre5, CodeBinaryOperatorType.BitwiseOr, cboe);
cmieCD.Parameters.Add(cboeCD);
cmieCD.Parameters.Add(new CodePrimitiveExpression(null));
cmieCD.Parameters.Add(careTarget);
CodeArrayCreateExpression caceCD = new CodeArrayCreateExpression(typeof(object));
CodeArgumentReferenceExpression careDelType = new CodeArgumentReferenceExpression(DELEGATETYPE);
CodeArgumentReferenceExpression careHandler = new CodeArgumentReferenceExpression(HANDLERARG);
caceCD.Initializers.Add(careDelType);
caceCD.Initializers.Add(careHandler);
cmieCD.Parameters.Add(caceCD);
cmieCD.Parameters.Add(new CodePrimitiveExpression(null));
CodeCastExpression cceCD = new CodeCastExpression(typeof(Delegate), cmieCD);
cmmCD.Statements.Add(new CodeMethodReturnStatement(cceCD));
GenerateXmlComments(cmmCD, cmmCD.Name);
ctdClass.Members.Add(cmmCD);
// protected override void AddEventHandler(EventInfo eventInfo, object target, Delegate handler);
// {
// eventInfo.AddEventHandler(target, handler);
// }
//
CodeMemberMethod cmmAEH = new CodeMemberMethod();
cmmAEH.Name = "AddEventHandler";
cmmAEH.Attributes = MemberAttributes.Family | MemberAttributes.Override;
param1 = new CodeParameterDeclarationExpression(typeof(EventInfo), EVENTINFO);
param3 = new CodeParameterDeclarationExpression(typeof(Delegate), HANDLERARG);
cmmAEH.Parameters.Add(param1);
cmmAEH.Parameters.Add(param2);
cmmAEH.Parameters.Add(param3);
CodeMethodReferenceExpression cmreAEH = new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression(EVENTINFO), "AddEventHandler");
CodeMethodInvokeExpression cmieAEH = new CodeMethodInvokeExpression();
cmieAEH.Method = cmreAEH;
cmieAEH.Parameters.Add(new CodeArgumentReferenceExpression(TARGET));
cmieAEH.Parameters.Add(new CodeArgumentReferenceExpression(HANDLERARG));
cmmAEH.Statements.Add(new CodeExpressionStatement(cmieAEH));
GenerateXmlComments(cmmAEH, cmmAEH.Name);
ctdClass.Members.Add(cmmAEH);
// }
//
cns.Types.Add(ctdClass);
// }
//
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(cns);
// For VB only we need to let the parser know about the RootNamespace value
// in order to look for the XamlGeneratedNamespace.GeneratedInternalTypeHelper
// type whose full type name would have been implicitly by the VB comopiler to
// RootNS.XamlGeneratedNamespace.GeneratedInternalTypeHelper
if (IsLanguageVB && !string.IsNullOrEmpty(DefaultNamespace))
{
// [assembly: RootNamespaceAttribute("RootNS")]
CodeAttributeDeclaration cad = new CodeAttributeDeclaration(
"System.Windows.Markup.RootNamespaceAttribute",
new CodeAttributeArgument(new CodePrimitiveExpression(DefaultNamespace)));
ccu.AssemblyCustomAttributes.Add(cad);
}
MemoryStream codeMemStream = new MemoryStream();
// using Disposes the StreamWriter when it ends. Disposing the StreamWriter
// also closes the underlying MemoryStream. Furthermore, don't add BOM here since
// TaskFileService.WriteGeneratedCodeFile adds it.
using (StreamWriter codeStreamWriter = new StreamWriter(codeMemStream, new UTF8Encoding(false)))
{
CodeGeneratorOptions o = new CodeGeneratorOptions();
CodeDomProvider codeProvider = EnsureCodeProvider();
codeProvider.GenerateCodeFromCompileUnit(ccu, codeStreamWriter, o);
codeStreamWriter.Flush();
TaskFileService.WriteGeneratedCodeFile(codeMemStream.ToArray(),
TargetPath + SharedStrings.GeneratedInternalTypeHelperFileName,
SharedStrings.GeneratedExtension, SharedStrings.IntellisenseGeneratedExtension,
LanguageSourceExtension);
}
}
internal string StartElement(ref string className, string subClassFullName, ref string modifier, Type elementType, string baseClassFullName)
{
string classFullName = null;
CodeContext cc = null;
if (_ccRoot == null)
{
if (className.Length > 0)
{
IsCodeNeeded = true;
}
else if (subClassFullName.Length > 0)
{
ThrowCompilerException(nameof(SR.MissingClassWithSubClass), DefinitionNSPrefix);
}
else if (modifier.Length > 0)
{
ThrowCompilerException(nameof(SR.MissingClassWithModifier), DefinitionNSPrefix);
}
else if (_typeArgsList != null)
{
string rootClassName = elementType != null ? elementType.Name : baseClassFullName.Substring(baseClassFullName.LastIndexOf(DOT, StringComparison.Ordinal)+1);
ThrowCompilerException(nameof(SR.MissingClassDefinitionForTypeArgs), rootClassName, DefinitionNSPrefix);
}
// Don't allow subclassing further from markup-subclasses with content
if (elementType != null && KnownTypes.Types[(int)KnownElements.IComponentConnector].IsAssignableFrom(elementType))
{
ThrowCompilerException(nameof(SR.SubSubClassingNotAllowed), elementType.FullName);
}
cc = GenerateSubClass(ref className, ref modifier, elementType, baseClassFullName);
Debug.Assert(_codeContexts.Count == 0, "mismatched codecontext");
_ccRoot = cc as CodeContextRoot;
Debug.Assert(_ccRoot != null);
if (IsCodeNeeded)
{
if (subClassFullName.Length > 0)
{
classFullName = subClassFullName;
_ccRoot.SubClass = classFullName;
}
else
{
classFullName = GetFullClassName(_ccRoot.CodeNS.Name, _ccRoot.CodeClass.Name);
}
if (IsLanguageVB)
{
// This classFullName is going to be used to write Root Start Element.
// If it is for VB, and DefaultClrName is set, we need to put DefaultClrName
// as prefix to any existing full class name.
//
// if this x:Class is set to "MyNS.MyPage, and RootNamespace (DefaultNamespace)
// is set to MyRoot, the finally generated class by VBC would be MyRoot.MyNS.MyPage.
// so in the Baml record, it should keep MyRoot.MyNS.MyPage.
//
classFullName = GetFullClassName(DefaultNamespace, classFullName);
}
}
if (IsCompilingEntryPointClass)
{
cc.IsAllowedNameScope = false;
}
}
else
{
cc = new CodeContext(elementType, null, baseClassFullName);
CodeContext ccParent = (CodeContext)_codeContexts.Peek();
cc.IsAllowedNameScope = ccParent.IsAllowedNameScope;
}
_codeContexts.Push(cc);
return classFullName;
}
internal void EndElement(bool pass2)
{
CodeContext cc = (CodeContext)_codeContexts.Pop();
Debug.Assert(cc != null);
if (_codeContexts.Count == 0)
{
Debug.Assert(_ccRoot == (cc as CodeContextRoot));
Debug.Assert(_ccRoot.CodeClass != null);
if (!pass2)
{
// For entry point class, a sub-class is always needed
// even if if wasn't otherwise needed by other markup
// like x:Code or events etc. upto this point.
if (IsCompilingEntryPointClass)
{
IsCodeNeeded = true;
}
if (IsCodeNeeded)
{
if (IsBamlNeeded)
{
GenerateInitializeComponent(IsCompilingEntryPointClass);
if (!IsCompilingEntryPointClass)
{
GenerateCreateDelegateHelper();
}
}
else
{
Debug.Assert(_ccRoot.HookupFn == null);
}
EndHookups();
EndStyleEventConnection();
}
GenerateSource();
}
}
}
internal void AddUsing(string clrNS)
{
if (String.IsNullOrEmpty(clrNS))
{
return;
}
if (_usingNS == null)
{
_usingNS = new ArrayList();
}
_usingNS.Add(clrNS);
}
#endregion CorePageGen
#region App Entry Point
// This code block is shared by regular Application and HostInBrowser Application
private CodeVariableReferenceExpression GenerateAppInstance(CodeMemberMethod cmmMain)
{
string appClassName = _ccRoot.SubClass.Length > 0 ? _ccRoot.SubClass
: GetFullClassName(_ccRoot.CodeNS.Name, _ccRoot.CodeClass.Name);
// MyNS.MyApplication app = new MyNS.MyApplication();
//
CodeObjectCreateExpression coce;
CodeVariableReferenceExpression cvre = new CodeVariableReferenceExpression(APPVAR);
CodeExpression[] ctorParams = {};
coce = new CodeObjectCreateExpression(appClassName, ctorParams);
CodeVariableDeclarationStatement cvds = new CodeVariableDeclarationStatement(appClassName, APPVAR, coce);
cmmMain.Statements.Add(cvds);
return cvre;
}
internal void AddApplicationProperty(MemberInfo memberInfo, string attributeValue, int lineNumber)
{
Debug.Assert(_ccRoot == (_codeContexts.Peek() as CodeContextRoot));
Debug.Assert(_ccRoot.ElementType == null ||
(memberInfo.DeclaringType.IsAssignableFrom(_ccRoot.ElementType) && (memberInfo is PropertyInfo)));
TypeConvertContext ctx = new TypeConvertContext(_parserContext, attributeValue);
CodeExpression ceValue = GetPropertyValueExpression(ctx, typeof(Uri), null, attributeValue);
CodeThisReferenceExpression ctreTag = new CodeThisReferenceExpression();
CodePropertyReferenceExpression cprePropSet = new CodePropertyReferenceExpression(ctreTag, memberInfo.Name);
CodeStatement csPropSet = new CodeAssignStatement(cprePropSet, ceValue);
AddLinePragma(csPropSet, lineNumber);
_ccRoot.EnsureInitializeComponentFn.Statements.Add(csPropSet);
}
internal void AddApplicationEvent(MarkupEventInfo mei)
{
// validate the event handler name per C# grammar for identifiers
ValidateEventHandlerName(mei.eventName, mei.eventHandler);
// this.FooEvent += new FooEventHandlerDelegate(this.OnFoo);
CodeThisReferenceExpression ctre = new CodeThisReferenceExpression();
CodeStatement csEvent = AddCLREvent(_ccRoot, ctre, mei);
Debug.Assert(_ccRoot == (_codeContexts.Peek() as CodeContextRoot));
_ccRoot.EnsureInitializeComponentFn.Statements.Add(csEvent);
}
private CodeMemberMethod GenerateEntryPointMethod()
{
CodeMemberMethod cmmMain = null;
CodeDomProvider codeProvider = EnsureCodeProvider();
if (codeProvider.Supports(GeneratorSupport.EntryPointMethod))
{
//
// [STAThread]
// public static void Main () {
//
cmmMain = new CodeEntryPointMethod();
cmmMain.Attributes = MemberAttributes.Public | MemberAttributes.Static;
cmmMain.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(STAThreadAttribute).FullName));
AddDebuggerNonUserCodeAttribute(cmmMain);
AddGeneratedCodeAttribute(cmmMain);
GenerateXmlComments(cmmMain, "Application Entry Point.");
cmmMain.ReturnType = new CodeTypeReference(typeof(void));
}
return cmmMain;
}
private void GenerateAppEntryPoint()
{
if (ApplicationFile.Length > 0)
{
// [STAThread]
// public static void Main () {
//
CodeMemberMethod cmmMain = GenerateEntryPointMethod();
if (cmmMain != null)
{
CodeVariableReferenceExpression cvreSplashScreen = null;
if (!string.IsNullOrEmpty(_splashImage) && !HostInBrowser)
{
cvreSplashScreen = GenerateSplashScreenInstance(cmmMain);
}
// MyApplication app = new MyApplication();
//
CodeVariableReferenceExpression cvreApp = GenerateAppInstance(cmmMain);
if (_ccRoot.InitializeComponentFn != null)
{
// app.InitializeComponent();
//
CodeMethodInvokeExpression cmieIT = new CodeMethodInvokeExpression();
cmieIT.Method = new CodeMethodReferenceExpression(cvreApp, INITIALIZE_COMPONENT);
cmmMain.Statements.Add(new CodeExpressionStatement(cmieIT));
}
if (!HostInBrowser)
{
// app.Run();
//
CodeMethodReferenceExpression cmreRun = new CodeMethodReferenceExpression(cvreApp, "Run");
CodeMethodInvokeExpression cmieRun = new CodeMethodInvokeExpression();
cmieRun.Method = cmreRun;
CodeStatement csRun = new CodeExpressionStatement(cmieRun);
cmmMain.Statements.Add(csRun);
}
_ccRoot.CodeClass.Members.Add(cmmMain);
}
}
}
private void GenerateLooseContentAttributes()
{
CodeDomProvider codeProvider = EnsureCodeProvider();
if (codeProvider.Supports(GeneratorSupport.AssemblyAttributes))
{
CodeCompileUnit ccu = new CodeCompileUnit();
foreach (string file in ContentList)
{
// [assembly: AssemblyAssociatedContentFileAttribute("file")]
string normalized = ResourceIDHelper.GetResourceIDFromRelativePath(file);
CodeAttributeDeclaration cad = new CodeAttributeDeclaration(
"System.Windows.Resources.AssemblyAssociatedContentFileAttribute",
new CodeAttributeArgument(new CodePrimitiveExpression(normalized)));
ccu.AssemblyCustomAttributes.Add(cad);
}
MemoryStream codeMemStream = new MemoryStream();
// using Disposes the StreamWriter when it ends. Disposing the StreamWriter
// also closes the underlying MemoryStream. Furthermore, don't add BOM here since
// TaskFileService.WriteGeneratedCodeFile adds it.
using (StreamWriter codeStreamWriter = new StreamWriter(codeMemStream, new UTF8Encoding(false)))
{
CodeGeneratorOptions o = new CodeGeneratorOptions();
codeProvider.GenerateCodeFromCompileUnit(ccu, codeStreamWriter, o);
codeStreamWriter.Flush();
TaskFileService.WriteGeneratedCodeFile(codeMemStream.ToArray(),
TargetPath + AssemblyName + SharedStrings.ContentFile,
SharedStrings.GeneratedExtension, SharedStrings.IntellisenseGeneratedExtension,
LanguageSourceExtension);
}
}
}
#endregion App Entry Point
#region Splash Screen Code Generation
private CodeVariableReferenceExpression GenerateSplashScreenInstance(CodeMemberMethod cmmMain)
{
// SplashScreen splashScreen = new SplashScreen(Assembly.GetExecutingAssembly(), "splash.png");
CodeObjectCreateExpression coceApplicationSplashScreen = new CodeObjectCreateExpression(SPLASHCLASSNAME, new CodePrimitiveExpression(GetSplashResourceId()));
// ApplicationSplashScreen splashScreen = ...
CodeVariableDeclarationStatement cvdsAppSplash = new CodeVariableDeclarationStatement(SPLASHCLASSNAME, SPLASHVAR, coceApplicationSplashScreen);
cmmMain.Statements.Add(cvdsAppSplash);
// splashScreen.Show(true);
CodeVariableReferenceExpression cvreAppSplash = new CodeVariableReferenceExpression(SPLASHVAR);
CodeMethodInvokeExpression cmieShowSplashScreen = new CodeMethodInvokeExpression(cvreAppSplash, "Show", new CodePrimitiveExpression(true));
cmmMain.Statements.Add(cmieShowSplashScreen);
return cvreAppSplash;
}
private string GetSplashResourceId()
{
// Perform the same resouce string mangle that is done in ResourceGenerator
string resourceId;
string fullFilePath = Path.GetFullPath(_splashImage);
string relPath = TaskHelper.GetRootRelativePath(TargetPath, fullFilePath);
//
// If the resFile is relative to the StagingDir (OutputPath here)
// take the relative path as resource id.
// If the resFile is not relative to StagingDir, but relative
// to the project directory, take this relative path as resource id.
// Otherwise, just take the file name as resource id.
//
if (string.IsNullOrEmpty(relPath))
{
relPath = TaskHelper.GetRootRelativePath(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar, fullFilePath);
}
if (string.IsNullOrEmpty(relPath) == false)
{
resourceId = relPath;
}
else
{
resourceId = Path.GetFileName(fullFilePath);
}
// Modify resource ID to correspond to canonicalized Uri format
// i.e. - all lower case, use "/" as separator
// ' ' is converted to escaped version %20
//
resourceId = ResourceIDHelper.GetResourceIDFromRelativePath(resourceId);
return resourceId;
}
#endregion
#region CodeContext
private class CodeContext
{
internal CodeContext(Type elementType, string [] typeArgsList, string localElementFullName)
{
_elementType = elementType;
_typeArgsList = typeArgsList;
_localElementFullName = localElementFullName;
}
internal Type ElementType
{
get { return _elementType; }
}
internal string ElementName
{
get { return _elementType != null ? _elementType.Name : _localElementFullName.Substring(_localElementFullName.LastIndexOf(DOT, StringComparison.Ordinal) + 1); }
}
internal string [] GenericTypeArgs
{
get { return _typeArgsList; }
}
internal string LocalElementFullName
{
get { return _localElementFullName; }
}
internal bool IsAllowedNameScope
{
get { return _isAllowedNameScope; }
set { _isAllowedNameScope = value; }
}
internal CodeTypeReference ElementTypeReference
{
get
{
if (_ctrElemTypeRef == null)
{
_ctrElemTypeRef = MarkupCompiler.GenerateConstructedTypeReference(_elementType, _typeArgsList, _localElementFullName);
}
return _ctrElemTypeRef;
}
set { _ctrElemTypeRef = value; }
}
private bool _isAllowedNameScope = true;
private Type _elementType = null;
private string [] _typeArgsList = null;
private string _localElementFullName = string.Empty;
protected CodeTypeReference _ctrElemTypeRef = null;
}
private class CodeContextRoot : CodeContext
{
internal CodeContextRoot(CodeTypeDeclaration codeClass,
CodeNamespace codeNS,
Type elementType,
string [] typeArgsList,
string localElementFullName) : base (elementType, typeArgsList, localElementFullName)
{
_codeNS = codeNS;
_codeClass = codeClass;
_ctrElemTypeRef = codeClass.BaseTypes[0];
}
internal CodeMemberMethod HookupFn
{
get { return _hookupFn; }
set { _hookupFn = value; }
}
internal CodeMemberMethod StyleConnectorFn
{
get { return _styleConnectorFn; }
set { _styleConnectorFn = value; }
}
internal CodeMemberMethod InitializeComponentFn
{
get { return _initializeComponentFn; }
}
internal CodeMemberMethod EnsureInitializeComponentFn
{
get
{
if (_initializeComponentFn == null)
{
_initializeComponentFn = new CodeMemberMethod();
_initializeComponentFn.Name = INITIALIZE_COMPONENT;
_initializeComponentFn.Attributes = MemberAttributes.Public | MemberAttributes.Final;
AddDebuggerNonUserCodeAttribute(_initializeComponentFn);
AddGeneratedCodeAttribute(_initializeComponentFn);
MarkupCompiler.GenerateXmlComments(_initializeComponentFn, INITIALIZE_COMPONENT);
_codeClass.Members.Add(_initializeComponentFn);
}
return _initializeComponentFn;
}
}
internal CodeTypeDeclaration CodeClass
{
get { return _codeClass; }
}
internal CodeNamespace CodeNS
{
get { return _codeNS; }
}
// This is used as the class to instantiate when a language does not support partial
// classes. A code-behind file needs to derive this class from the generated sub-class
// that is normally specified by the x:Class attribute.
internal string SubClass
{
get { return _subClass; }
set { _subClass = value; }
}
private CodeTypeDeclaration _codeClass;
private CodeNamespace _codeNS;
private CodeMemberMethod _initializeComponentFn = null;
private CodeMemberMethod _hookupFn = null;
private CodeMemberMethod _styleConnectorFn = null;
private string _subClass = string.Empty;
}
#endregion CodeContext
#endregion Implementation
#region Private Data
private string _targetPath = string.Empty; // Current Dir is default
private string[] _contentList = null;
private ArrayList _referenceAssemblyList = null;
private string _localXamlApplication = null;
private string[] _localXamlPages = null;
private string [] _typeArgsList = null;
private ArrayList _pendingLocalFiles = null;
private bool _hostInBrowser = false;
private bool _xamlDebuggingInformation = false;
private string _splashImage = null;
private bool _isLangCSharp = false;
private bool _isLangVB = false;
private string _language = string.Empty;
private string _languageSourceExtension = string.Empty;
private static string _definitionNSPrefix = DEFINITION_PREFIX;
private CompilerInfo _ci = null;
private CodeDomProvider _codeProvider = null;
private XamlTypeMapper _typeMapper = null;
private bool _isCompilingEntryPointClass = false;
private bool _isBamlNeeded = false;
private bool _isCodeNeeded = false;
private bool _hasLocalEvent = false;
private bool _hasGeneratedInternalTypeHelper = false;
private string _assemblyName = string.Empty;
private string _assemblyVersion = string.Empty;
private string _assemblyPublicKeyToken = string.Empty;
private string _applicationFile = string.Empty;
private string _defaultNamespace = string.Empty;
private ParserHooks _parserHooks;
private SourceFileInfo _sourceFileInfo = null;
private string _compilationUnitSourcePath = string.Empty;
private ArrayList _usingNS = null;
private Assembly _localAssembly = null;
private ReferenceAssembly _localAssemblyFile = null;
private ParserContext _parserContext = null;
private CodeContextRoot _ccRoot = null;
private Stack _codeContexts = null;
private ITaskFileService _taskFileService = null;
private TaskLoggingHelper _taskLogger = null;
private bool _hasEmittedEventSetterDeclaration;
// Per language sccess Modfiers
private string _private = string.Empty;
private string _public = string.Empty;
private string _protected = string.Empty;
private string _internal = string.Empty;
private string _protectedInternal = string.Empty;
private string _privateClass = string.Empty;
private string _publicClass = string.Empty;
// Prefixes & Tags
private const string INDENT12 = " ";
private const string ANONYMOUS_ENTRYCLASS_PREFIX = "Generated";
private const string DEFINITION_PREFIX = "x";
private const char COMMA = ',';
private const char GENERIC_DELIMITER = '`';
internal const char DOTCHAR = '.';
internal const string DOT = ".";
internal const string CODETAG = "Code";
// Language support
private const string XAML = ".xaml";
private const string BAML = ".baml";
private const string VB = "vb";
private const string CSHARP = "c#";
private const string JSHARP = "vj#";
private const string JSCRIPT = "js";
// Generated identifiers
private const string CREATEDELEGATEHELPER = "_CreateDelegate";
private const string CONTENT_LOADED = "_contentLoaded";
private const string CONNECT = "Connect";
private const string CONNECTIONID = "connectionId";
private const string TARGET = "target";
private const string EVENTSETTER = "eventSetter";
private const string EVENT = "Event";
private const string ADDHANDLER = "AddHandler";
private const string HELPER = "Helper";
private const string HANDLERARG = "handler";
private const string TYPE = "type";
private const string CULTURE = "culture";
private const string DEFAULT = "Default";
private const string VALUE = "value";
private const string DELEGATETYPE = "delegateType";
private const string PROPINFO = "propertyInfo";
private const string EVENTINFO = "eventInfo";
private const string APPVAR = "app";
private const string SPLASHVAR = "splashScreen";
private const string SPLASHCLASSNAME = "SplashScreen";
private const string ARGS = "args";
private const string INITIALIZE_COMPONENT = "InitializeComponent";
private const string SWITCH_STATEMENT = $"{INDENT12}switch ({CONNECTIONID})\r\n{INDENT12}{{";
private const string BREAK_STATEMENT = $"{INDENT12}break;";
private const string CASE_STATEMENT = $"{INDENT12}case ";
private const string ENDCURLY = "}";
private const string COLON = ":";
private const string RESOURCE_LOCATER = "resourceLocater";
private const string LOADCOMPONENT = "LoadComponent";
private const string SETTERS = "Setters";
private const string SummaryStartTag = @"<summary>";
private const string SummaryEndTag = @"</summary>";
internal const string ADD = "Add";
internal const string HANDLER = "Handler";
// Delimiters & Uri processing
private const string VER = "V";
private const string COMPONENT = "component";
private const char COMPONENT_DELIMITER = ';';
private const string FORWARDSLASH = "/";
private const string URISCHEME_PACK = "pack";
private const string PARENTFOLDER = @"..\";
// For generating pragma checksum data
private static readonly Guid s_hashSHA256Guid = new Guid(0x8829d00f, 0x11b8, 0x4213, 0x87, 0x8b, 0x77, 0x0e, 0x85, 0x97, 0xac, 0x16);
private static readonly Guid s_hashSHA1Guid = new Guid(0xff1816ec, 0xaa5e, 0x4d10, 0x87, 0xf7, 0x6f, 0x49, 0x63, 0x83, 0x34, 0x60);
private static string s_generatedCode_ToolName;
private static string s_generatedCode_ToolVersion;
#endregion Private Data
}
}
|