From 1d1e209cb2617541c37ec5955908c13acf8fa132 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 19 Aug 2015 15:31:42 -0700 Subject: [PATCH 1/9] Port FileUtilities to .NET Core --- dir.props | 2 + src/Shared/CommunicationsUtilities.cs | 2 +- src/Shared/ExceptionHandling.cs | 4 +- src/Shared/FileUtilities.cs | 56 ++++++++++++++----- src/Shared/TaskLoggingHelper.cs | 2 +- .../BuildRequestEngine/BuildRequestEngine.cs | 2 +- .../BackEnd/Components/Scheduler/Scheduler.cs | 8 +-- .../Construction/Solution/SolutionFile.cs | 4 +- src/XMakeBuildEngine/Logging/FileLogger.cs | 4 +- 9 files changed, 58 insertions(+), 26 deletions(-) diff --git a/dir.props b/dir.props index e9bc848348e..83d8c8eef7b 100644 --- a/dir.props +++ b/dir.props @@ -104,12 +104,14 @@ $(DefineConstants);FEATURE_APPDOMAIN $(DefineConstants);FEATURE_APPDOMAIN_UNHANDLED_EXCEPTION $(DefineConstants);FEATURE_ASSEMBLY_LOADFROM + $(DefineConstants);FEATURE_ASSEMBLY_LOCATION $(DefineConstants);FEATURE_ASSEMBLYNAME_CULTUREINFO $(DefineConstants);FEATURE_ASSEMBLYNAME_CLONE $(DefineConstants);FEATURE_BINARY_SERIALIZATION true $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION $(DefineConstants);FEATURE_GAC + $(DefineConstants);FEATURE_GET_COMMANDLINE $(DefineConstants);FEATURE_REFLECTION_EMIT_DEBUG_INFO $(DefineConstants);FEATURE_RESOURCE_EXPOSURE $(DefineConstants);FEATURE_SECURITY_PERMISSIONS diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index e9dc8e04fbf..2487415959f 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -583,7 +583,7 @@ internal static void Trace(int nodeId, string format, params object[] args) fileName += ".txt"; - using (StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId))) + using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, args); long now = DateTime.UtcNow.Ticks; diff --git a/src/Shared/ExceptionHandling.cs b/src/Shared/ExceptionHandling.cs index 8c7bb2935e4..725d3e926dc 100644 --- a/src/Shared/ExceptionHandling.cs +++ b/src/Shared/ExceptionHandling.cs @@ -319,14 +319,14 @@ internal static void DumpExceptionToFile(Exception ex) s_dumpFileName = Path.Combine(tempPath, "MSBuild_" + guid.ToString() + ".failure.txt"); - using (StreamWriter writer = FileUtilities.OpenFileForAppend(s_dumpFileName)) + using (StreamWriter writer = FileUtilities.OpenFile(s_dumpFileName, append: true)) { writer.WriteLine("UNHANDLED EXCEPTIONS FROM PROCESS {0}:", Process.GetCurrentProcess().Id); writer.WriteLine("====================="); } } - using (StreamWriter writer = FileUtilities.OpenFileForAppend(s_dumpFileName)) + using (StreamWriter writer = FileUtilities.OpenFile(s_dumpFileName, append: true)) { // "G" format is, e.g., 6/15/2008 9:15:07 PM writer.WriteLine(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)); diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index 2731ecbd656..823ab528c02 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -54,20 +54,25 @@ internal static partial class FileUtilities /// private static void GetTestExecutionInfo() { + +#if FEATURE_GET_COMMANDLINE // Get the executable we are running var program = Path.GetFileNameWithoutExtension(Environment.GetCommandLineArgs()[0]); +#else + var program = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); +#endif // Check if it matches the pattern s_runningTests = program != null && s_testRunners.Any( - s => program.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) == -1); + s => program.IndexOf(s, StringComparison.OrdinalIgnoreCase) == -1); // Does not look like it's a test, but check the process name if (!s_runningTests) { program = Process.GetCurrentProcess().ProcessName; s_runningTests = - s_testRunners.Any(s => program.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) == -1); + s_testRunners.Any(s => program.IndexOf(s, StringComparison.OrdinalIgnoreCase) == -1); } // Definitely not a test, leave @@ -88,7 +93,7 @@ private static void GetTestExecutionInfo() if (dir == null) { // Can't get the assembly path, use current directory - dir = Environment.CurrentDirectory; + dir = Directory.GetCurrentDirectory(); } else { @@ -539,7 +544,7 @@ internal static bool HasExtension(string fileName, string[] allowedExtensions) string fileExtension = Path.GetExtension(fileName); foreach (string extension in allowedExtensions) { - if (String.Compare(fileExtension, extension, true /* ignore case */, CultureInfo.CurrentCulture) == 0) + if (String.Compare(fileExtension, extension, StringComparison.CurrentCultureIgnoreCase) == 0) { return true; } @@ -562,17 +567,31 @@ internal static string ExecutingAssemblyPath { get { +#if FEATURE_ASSEMBLY_LOCATION try { - return Path.GetFullPath(new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath); + return Path.GetFullPath(new Uri(typeof(FileUtilities).GetTypeInfo().Assembly.EscapedCodeBase).LocalPath); } catch (InvalidOperationException e) { // Workaround for issue where people are getting relative uri crash here. // Last resort. We may have a problem when the assembly is shadow-copied. ExceptionHandling.DumpExceptionToFile(e); - return System.Reflection.Assembly.GetExecutingAssembly().Location; + return typeof(FileUtilities).GetTypeInfo().Assembly.Location; } +#else + // If we can't get the path to an assembly (ie on .NET Core), then assume that this assembly has been + // loaded from the same directory as the main application + var appPath = Process.GetCurrentProcess().MainModule.FileName; + var appDirectory = Path.GetDirectoryName(appPath); + var assemblyPath = Path.Combine(appDirectory, typeof(FileUtilities).GetTypeInfo().Assembly.GetName().Name + ".dll"); + assemblyPath = Path.GetFullPath(assemblyPath); + if (!File.Exists(assemblyPath)) + { + throw new FileNotFoundException(assemblyPath); + } + return assemblyPath; +#endif } } @@ -613,9 +632,12 @@ internal static string CurrentExecutablePath } else { - s_executablePath = Environment.GetCommandLineArgs()[0] ?? Path.Combine( - Path.GetDirectoryName(ExecutingAssemblyPath) ?? Environment.CurrentDirectory, - "MSBuild.exe"); + + s_executablePath = +#if FEATURE_GET_COMMANDLINE + Environment.GetCommandLineArgs()[0] ?? +#endif + Path.Combine(Path.GetDirectoryName(ExecutingAssemblyPath) ?? Directory.GetCurrentDirectory(), "MSBuild.exe"); } } @@ -1035,7 +1057,7 @@ internal static string AttemptToShortenPath(string path) { // >= not > because MAX_PATH assumes a trailing null if (path.Length >= NativeMethodsShared.MAX_PATH || - (!IsRootedNoThrow(path) && ((Environment.CurrentDirectory.Length + path.Length + 1 /* slash */) >= NativeMethodsShared.MAX_PATH))) + (!IsRootedNoThrow(path) && ((Directory.GetCurrentDirectory().Length + path.Length + 1 /* slash */) >= NativeMethodsShared.MAX_PATH))) { // Attempt to make it shorter -- perhaps there are some \..\ elements path = GetFullPathNoThrow(path); @@ -1077,11 +1099,19 @@ private static string CurrentExecutableOverride } } - internal static StreamWriter OpenFileForAppend(string path) + internal static StreamWriter OpenFile(string path, bool append, Encoding encoding = null) { const int DefaultFileStreamBufferSize = 4096; - Stream fileStream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); - return new StreamWriter(fileStream); + FileMode mode = append? FileMode.Append: FileMode.Create; + Stream fileStream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); + if (encoding == null) + { + return new StreamWriter(fileStream); + } + else + { + return new StreamWriter(fileStream, encoding); + } } } } diff --git a/src/Shared/TaskLoggingHelper.cs b/src/Shared/TaskLoggingHelper.cs index ae1bcb91490..43d412d4e5c 100644 --- a/src/Shared/TaskLoggingHelper.cs +++ b/src/Shared/TaskLoggingHelper.cs @@ -1235,7 +1235,7 @@ public bool LogMessagesFromFile(string fileName, MessageImportance messageImport // Command-line tools are generally going to emit their output using the current // codepage, so that it displays correctly in the console window. - using (StreamReader fileStream = new StreamReader(fileName, System.Text.Encoding.Default)) // HIGHCHAR: Use ANSI for logging messages. + using (StreamReader fileStream = new StreamReader(fileName, System.Text.Encoding.GetEncoding(0))) // HIGHCHAR: Use ANSI for logging messages. { errorsFound = LogMessagesFromStream(fileStream, messageImportance); } diff --git a/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index ce89ff50970..b756a4a30d7 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -1351,7 +1351,7 @@ private void TraceEngine(string format, params object[] stuff) { lock (this) { - using (StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, @"EngineTrace_{0}.txt"), Process.GetCurrentProcess().Id))) + using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, @"EngineTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, stuff); file.WriteLine("{0}({1})-{2}: {3}", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, DateTime.UtcNow.Ticks, message); diff --git a/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs b/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs index 7a0cad9df8d..6be1cdd6ee2 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs @@ -2193,7 +2193,7 @@ private void TraceScheduler(string format, params object[] stuff) { if (_debugDumpState) { - StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), Process.GetCurrentProcess().Id)); + StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true); file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, _schedulingData.EventTime.Ticks); file.WriteLine(format, stuff); file.Flush(); @@ -2210,7 +2210,7 @@ private void DumpSchedulerState() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id))) + using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Scheduler state at timestamp {0}:", _schedulingData.EventTime.Ticks); file.WriteLine("------------------------------------------------", _schedulingData.EventTime.Ticks); @@ -2307,7 +2307,7 @@ private void DumpConfigurations() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id))) + using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Configurations used during this build"); file.WriteLine("-------------------------------------"); @@ -2341,7 +2341,7 @@ private void DumpRequests() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFileForAppend(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id))) + using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Requests used during the build:"); file.WriteLine("-------------------------------"); diff --git a/src/XMakeBuildEngine/Construction/Solution/SolutionFile.cs b/src/XMakeBuildEngine/Construction/Solution/SolutionFile.cs index 1da1d433265..5d46a163994 100644 --- a/src/XMakeBuildEngine/Construction/Solution/SolutionFile.cs +++ b/src/XMakeBuildEngine/Construction/Solution/SolutionFile.cs @@ -355,7 +355,7 @@ internal static void GetSolutionFileAndVisualStudioMajorVersions(string solution { // Open the file fileStream = File.OpenRead(solutionFile); - reader = new StreamReader(fileStream, Encoding.Default); // HIGHCHAR: If solution files have no byte-order marks, then assume ANSI rather than ASCII. + reader = new StreamReader(fileStream, Encoding.GetEncoding(0)); // HIGHCHAR: If solution files have no byte-order marks, then assume ANSI rather than ASCII. // Read first 4 lines of the solution file. // The header is expected to be in line 1 or 2 @@ -500,7 +500,7 @@ internal void ParseSolutionFile() fileStream = File.OpenRead(_solutionFile); // Store the directory of the file as the current directory may change while we are processes the file _solutionFileDirectory = Path.GetDirectoryName(_solutionFile); - _reader = new StreamReader(fileStream, Encoding.Default); // HIGHCHAR: If solution files have no byte-order marks, then assume ANSI rather than ASCII. + _reader = new StreamReader(fileStream, Encoding.GetEncoding(0)); // HIGHCHAR: If solution files have no byte-order marks, then assume ANSI rather than ASCII. this.ParseSolution(); } catch (Exception e) diff --git a/src/XMakeBuildEngine/Logging/FileLogger.cs b/src/XMakeBuildEngine/Logging/FileLogger.cs index 4794084d5ee..8927b5ab467 100644 --- a/src/XMakeBuildEngine/Logging/FileLogger.cs +++ b/src/XMakeBuildEngine/Logging/FileLogger.cs @@ -80,7 +80,7 @@ private void InitializeFileLogger(IEventSource eventSource, int nodeCount) try { - _fileWriter = new StreamWriter(_logFileName, _append, _encoding); + _fileWriter = FileUtilities.OpenFile(_logFileName, _append, _encoding); _fileWriter.AutoFlush = _autoFlush; } @@ -245,7 +245,7 @@ private void ApplyFileLoggerParameter(string parameterName, string parameterValu /// /// Encoding for the output. Defaults to ANSI. /// - private Encoding _encoding = Encoding.Default; + private Encoding _encoding = Encoding.GetEncoding(0); /// /// File logger parameters delimiters. From 8631507fcc6c578141701eaca25befafdcd0ba0e Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 19 Aug 2015 18:03:39 -0700 Subject: [PATCH 2/9] Port FrameworkLocationHelper to .NET Core --- NuGet.Config | 13 + dir.props | 4 + src/Shared/FileUtilities.GetFolderPath.cs | 235 ++++++++++++++++++ src/Shared/FrameworkLocationHelper.cs | 19 +- src/Shared/NativeMethodsShared.cs | 16 ++ src/Shared/Tracing.cs | 2 +- .../Communications/NodeProviderOutOfProc.cs | 3 +- .../TaskExecutionHost/TaskExecutionHost.cs | 4 +- .../XmlDocumentWithLocation.cs | 8 +- .../Evaluation/ExpressionShredder.cs | 2 + src/XMakeBuildEngine/Microsoft.Build.csproj | 3 + src/XMakeBuildEngine/Utilities/Utilities.cs | 8 + src/XMakeBuildEngine/project.json | 4 +- src/XMakeBuildEngine/project.lock.json | 178 +++++++++++-- 14 files changed, 477 insertions(+), 22 deletions(-) create mode 100644 NuGet.Config create mode 100644 src/Shared/FileUtilities.GetFolderPath.cs diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 00000000000..d43bd47aadf --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/dir.props b/dir.props index 83d8c8eef7b..e88059b8e8f 100644 --- a/dir.props +++ b/dir.props @@ -112,9 +112,13 @@ $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION $(DefineConstants);FEATURE_GAC $(DefineConstants);FEATURE_GET_COMMANDLINE + $(DefineConstants);FEATURE_OSVERSION $(DefineConstants);FEATURE_REFLECTION_EMIT_DEBUG_INFO $(DefineConstants);FEATURE_RESOURCE_EXPOSURE $(DefineConstants);FEATURE_SECURITY_PERMISSIONS + $(DefineConstants);FEATURE_SPECIAL_FOLDERS + true + $(DefineConstants);FEATURE_SYSTEM_CONFIGURATION $(DefineConstants);FEATURE_THREAD_ABORT $(DefineConstants);FEATURE_THREAD_CULTURE $(DefineConstants);FEATURE_THREAD_PRIORITY diff --git a/src/Shared/FileUtilities.GetFolderPath.cs b/src/Shared/FileUtilities.GetFolderPath.cs new file mode 100644 index 00000000000..c7c37b318be --- /dev/null +++ b/src/Shared/FileUtilities.GetFolderPath.cs @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Globalization; +using System.Linq; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Text; +using System.Threading; + +namespace Microsoft.Build.Shared +{ + internal static partial class FileUtilities + { + // .NET Core doesn't currently have APIs to get the "special" folders (ie the ones defined in the Environment.SpecialFolder enum) + // The code here is mostly copied out of the .NET reference source for this functionality + + public static string GetFolderPath(SpecialFolder folder) + { + if (!NativeMethodsShared.IsWindows) + { + throw new PlatformNotSupportedException(); + } + + SpecialFolderOption option = SpecialFolderOption.None; + + StringBuilder sb = new StringBuilder(NativeMethodsShared.MAX_PATH); + int hresult = Win32Native.SHGetFolderPath(IntPtr.Zero, /* hwndOwner: [in] Reserved */ + ((int)folder | (int)option), /* nFolder: [in] CSIDL */ + IntPtr.Zero, /* hToken: [in] access token */ + Win32Native.SHGFP_TYPE_CURRENT, /* dwFlags: [in] retrieve current path */ + sb); /* pszPath: [out]resultant path */ + + String s; + if (hresult < 0) + { + switch (hresult) + { + default: + // The previous incarnation threw away all errors. In order to limit + // breaking changes, we will be permissive about these errors + // instead of calling ThowExceptionForHR. + //Runtime.InteropServices.Marshal.ThrowExceptionForHR(hresult); + break; + case __HResults.COR_E_PLATFORMNOTSUPPORTED: + // This one error is the one we do want to throw. + // + + throw new PlatformNotSupportedException(); + } + + // SHGetFolderPath does not initialize the output buffer on error + s = String.Empty; + } + else + { + s = sb.ToString(); + } + + return s; + } + + internal const String SHELL32 = "shell32.dll"; + + internal static class __HResults + { + internal const int COR_E_PLATFORMNOTSUPPORTED = unchecked((int)0x80131539); + } + + private static class Win32Native + { + [DllImport(SHELL32, CharSet = CharSet.Unicode, BestFitMapping = false)] + internal static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, [Out]StringBuilder lpszPath); + + internal const int SHGFP_TYPE_CURRENT = 0; // the current (user) folder path setting + + internal const int CSIDL_APPDATA = 0x001a; + internal const int CSIDL_COMMON_APPDATA = 0x0023; + internal const int CSIDL_LOCAL_APPDATA = 0x001c; + internal const int CSIDL_COOKIES = 0x0021; + internal const int CSIDL_FAVORITES = 0x0006; + internal const int CSIDL_HISTORY = 0x0022; + internal const int CSIDL_INTERNET_CACHE = 0x0020; + internal const int CSIDL_PROGRAMS = 0x0002; + internal const int CSIDL_RECENT = 0x0008; + internal const int CSIDL_SENDTO = 0x0009; + internal const int CSIDL_STARTMENU = 0x000b; + internal const int CSIDL_STARTUP = 0x0007; + internal const int CSIDL_SYSTEM = 0x0025; + internal const int CSIDL_TEMPLATES = 0x0015; + internal const int CSIDL_DESKTOPDIRECTORY = 0x0010; + internal const int CSIDL_PERSONAL = 0x0005; + internal const int CSIDL_PROGRAM_FILES = 0x0026; + internal const int CSIDL_PROGRAM_FILES_COMMON = 0x002b; + internal const int CSIDL_DESKTOP = 0x0000; + internal const int CSIDL_DRIVES = 0x0011; + internal const int CSIDL_MYMUSIC = 0x000d; + internal const int CSIDL_MYPICTURES = 0x0027; + + internal const int CSIDL_PROGRAM_FILESX86 = 0x002a; // x86 C:\Program Files on RISC + + internal const int CSIDL_WINDOWS = 0x0024; // GetWindowsDirectory() + + internal const int CSIDL_FLAG_CREATE = 0x8000; // force folder creation in SHGetFolderPath + internal const int CSIDL_FLAG_DONT_VERIFY = 0x4000; // return an unverified folder path + } + + [ComVisible(true)] + public enum SpecialFolder + { + // + // Represents the file system directory that serves as a common repository for + // application-specific data for the current, roaming user. + // A roaming user works on more than one computer on a network. A roaming user's + // profile is kept on a server on the network and is loaded onto a system when the + // user logs on. + // + ApplicationData = Win32Native.CSIDL_APPDATA, + // + // Represents the file system directory that serves as a common repository for application-specific data that + // is used by all users. + // + CommonApplicationData = Win32Native.CSIDL_COMMON_APPDATA, + // + // Represents the file system directory that serves as a common repository for application specific data that + // is used by the current, non-roaming user. + // + LocalApplicationData = Win32Native.CSIDL_LOCAL_APPDATA, + // + // Represents the file system directory that serves as a common repository for Internet + // cookies. + // + Cookies = Win32Native.CSIDL_COOKIES, + Desktop = Win32Native.CSIDL_DESKTOP, + // + // Represents the file system directory that serves as a common repository for the user's + // favorite items. + // + Favorites = Win32Native.CSIDL_FAVORITES, + // + // Represents the file system directory that serves as a common repository for Internet + // history items. + // + History = Win32Native.CSIDL_HISTORY, + // + // Represents the file system directory that serves as a common repository for temporary + // Internet files. + // + InternetCache = Win32Native.CSIDL_INTERNET_CACHE, + // + // Represents the file system directory that contains + // the user's program groups. + // + Programs = Win32Native.CSIDL_PROGRAMS, + MyComputer = Win32Native.CSIDL_DRIVES, + MyMusic = Win32Native.CSIDL_MYMUSIC, + MyPictures = Win32Native.CSIDL_MYPICTURES, + //// "My Videos" folder + //MyVideos = Win32Native.CSIDL_MYVIDEO, + // + // Represents the file system directory that contains the user's most recently used + // documents. + // + Recent = Win32Native.CSIDL_RECENT, + // + // Represents the file system directory that contains Send To menu items. + // + SendTo = Win32Native.CSIDL_SENDTO, + // + // Represents the file system directory that contains the Start menu items. + // + StartMenu = Win32Native.CSIDL_STARTMENU, + // + // Represents the file system directory that corresponds to the user's Startup program group. The system + // starts these programs whenever any user logs on to Windows NT, or + // starts Windows 95 or Windows 98. + // + Startup = Win32Native.CSIDL_STARTUP, + // + // System directory. + // + System = Win32Native.CSIDL_SYSTEM, + // + // Represents the file system directory that serves as a common repository for document + // templates. + // + Templates = Win32Native.CSIDL_TEMPLATES, + // + // Represents the file system directory used to physically store file objects on the desktop. + // This should not be confused with the desktop folder itself, which is + // a virtual folder. + // + DesktopDirectory = Win32Native.CSIDL_DESKTOPDIRECTORY, + // + // Represents the file system directory that serves as a common repository for documents. + // + Personal = Win32Native.CSIDL_PERSONAL, + // + // "MyDocuments" is a better name than "Personal" + // + MyDocuments = Win32Native.CSIDL_PERSONAL, + // + // Represents the program files folder. + // + ProgramFiles = Win32Native.CSIDL_PROGRAM_FILES, + // + // Represents the folder for components that are shared across applications. + // + CommonProgramFiles = Win32Native.CSIDL_PROGRAM_FILES_COMMON, + + // + // x86 C:\Program Files on RISC + // + ProgramFilesX86 = Win32Native.CSIDL_PROGRAM_FILESX86, + + // + // GetWindowsDirectory() + // + Windows = Win32Native.CSIDL_WINDOWS, + } + + private enum SpecialFolderOption + { + None = 0, + Create = Win32Native.CSIDL_FLAG_CREATE, + DoNotVerify = Win32Native.CSIDL_FLAG_DONT_VERIFY, + } + } +} diff --git a/src/Shared/FrameworkLocationHelper.cs b/src/Shared/FrameworkLocationHelper.cs index e5cdb60dd1e..8899d52cdbd 100644 --- a/src/Shared/FrameworkLocationHelper.cs +++ b/src/Shared/FrameworkLocationHelper.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +#if FEATURE_SYSTEM_CONFIGURATION using System.Configuration; +#endif using System.IO; using System.Linq; using System.Runtime.Versioning; @@ -14,6 +16,7 @@ using PropertyElement = Microsoft.Build.Evaluation.ToolsetElement.PropertyElement; using System.Reflection; +using System.Runtime.InteropServices; namespace Microsoft.Build.Shared { @@ -129,7 +132,11 @@ internal static class FrameworkLocationHelper /// By default when a root path is not specified we would like to use the program files directory \ reference assemblies\framework as the root location /// to generate the reference assembly paths from. /// +#if FEATURE_SPECIAL_FOLDERS internal static readonly string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); +#else + internal static readonly string programFiles = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.ProgramFiles); +#endif internal static readonly string programFiles32 = GenerateProgramFiles32(); internal static readonly string programFiles64 = GenerateProgramFiles64(); internal static readonly string programFilesReferenceAssemblyLocation = GenerateProgramFilesReferenceAssemblyRoot(); @@ -388,7 +395,9 @@ private static string FallbackDotNetFrameworkSdkInstallPath fallbackDotNetFrameworkSdkRegistryInstallPath, fallbackDotNetFrameworkSdkInstallKeyValue); - if (Environment.Is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) + bool is64BitProcess = Marshal.SizeOf() == 8; + + if (is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) { // Since we're 64-bit, what we just checked was the 64-bit fallback key -- so now let's // check the 32-bit one too, just in case. @@ -650,7 +659,7 @@ DotNetFrameworkArchitecture architecture if (!NativeMethodsShared.IsWindows) { if (!string.IsNullOrEmpty(prefix) - && prefix.Substring(0, 1).Equals("v", StringComparison.InvariantCultureIgnoreCase)) + && prefix.Substring(0, 1).Equals("v", StringComparison.OrdinalIgnoreCase)) { prefix = prefix.Substring(1); } @@ -756,7 +765,11 @@ internal static string GenerateProgramFiles32() // On a 64 bit machine we always want to use the program files x86. If we are running as a 64 bit process then this variable will be set correctly // If we are on a 32 bit machine or running as a 32 bit process then this variable will be null and the programFiles variable will be correct. +#if FEATURE_SPECIAL_FOLDERS string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); +#else + string programFilesX86 = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.ProgramFilesX86); +#endif if (String.IsNullOrEmpty(programFilesX86)) { // 32 bit box @@ -934,6 +947,7 @@ private static string GetPathToBuildToolsFromConfig(string toolsVersion) { string toolPath = null; +#if FEATURE_SYSTEM_CONFIGURATION if (ToolsetConfigurationReaderHelpers.ConfigurationFileMayHaveToolsets()) { try @@ -980,6 +994,7 @@ private static string GetPathToBuildToolsFromConfig(string toolsVersion) // to see if there is any valid data there. } } +#endif return toolPath; } diff --git a/src/Shared/NativeMethodsShared.cs b/src/Shared/NativeMethodsShared.cs index 61e251c29cc..16d4b9b43a2 100644 --- a/src/Shared/NativeMethodsShared.cs +++ b/src/Shared/NativeMethodsShared.cs @@ -320,8 +320,12 @@ internal static bool IsUnixLike { get { +#if FEATURE_OSVERSION var env = Environment.OSVersion.Platform; return env == PlatformID.MacOSX || env == PlatformID.Unix; +#else + return IsUnix || IsOSX; +#endif } } @@ -332,7 +336,11 @@ internal static bool IsUnix { get { +#if FEATURE_OSVERSION return Environment.OSVersion.Platform == PlatformID.Unix; +#else + return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); +#endif } } @@ -354,7 +362,11 @@ internal static bool IsWindows { get { +#if FEATURE_OSVERSION return !IsUnixLike; +#else + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif } } @@ -365,7 +377,11 @@ internal static bool IsOSX { get { +#if FEATURE_OSVERSION return Environment.OSVersion.Platform == PlatformID.MacOSX; +#else + return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); +#endif } } diff --git a/src/Shared/Tracing.cs b/src/Shared/Tracing.cs index 4d7484fef1e..a6d2fa7803b 100644 --- a/src/Shared/Tracing.cs +++ b/src/Shared/Tracing.cs @@ -69,7 +69,7 @@ static Tracing() s_interval = TimeSpan.FromSeconds(1); } - s_currentAssemblyName = Assembly.GetExecutingAssembly().GetName().Name; + s_currentAssemblyName = typeof(Tracing).GetTypeInfo().Assembly.GetName().Name; // Trace.WriteLine(new string('/', 100)); // Trace.WriteLine("interval: " + interval.Seconds); diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs index af18d1a7e76..019bb73fb25 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs @@ -76,7 +76,8 @@ internal static long HostHandshake get { long baseHandshake = Constants.AssemblyTimestamp; - if (Environment.Is64BitProcess) + bool is64BitProcess = Marshal.SizeOf() == 8; + if (is64BitProcess) { unchecked { diff --git a/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index c267040f487..9c6e9201797 100644 --- a/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -966,12 +966,12 @@ private TaskFactoryWrapper FindTaskInRegistry(IDictionary taskId // Map to an intrinsic task, if necessary. if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "Microsoft.Build.Tasks.MSBuild", StringComparison.OrdinalIgnoreCase)) { - returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(Assembly.GetExecutingAssembly().FullName, null)), _taskName, null); + returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(MSBuild)), new LoadedType(typeof(MSBuild), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null)), _taskName, null); _intrinsicTasks[_taskName] = returnClass; } else if (String.Equals(returnClass.TaskFactory.TaskType.FullName, "Microsoft.Build.Tasks.CallTarget", StringComparison.OrdinalIgnoreCase)) { - returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(Assembly.GetExecutingAssembly().FullName, null)), _taskName, null); + returnClass = new TaskFactoryWrapper(new IntrinsicTaskFactory(typeof(CallTarget)), new LoadedType(typeof(CallTarget), AssemblyLoadInfo.Create(typeof(TaskExecutionHost).GetTypeInfo().Assembly.FullName, null)), _taskName, null); _intrinsicTasks[_taskName] = returnClass; } } diff --git a/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs b/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs index 761b7f02075..b5b460c903c 100644 --- a/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs +++ b/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs @@ -354,7 +354,13 @@ private void DetermineWhetherToLoadReadOnly(string fullPath) ErrorUtilities.VerifyThrow(Path.IsPathRooted(fullPath), "should be full path"); string directory = Path.GetDirectoryName(fullPath); - if (directory.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.Windows), StringComparison.OrdinalIgnoreCase) || +#if FEATURE_SPECIAL_FOLDERS + string windowsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Windows); +#else + string windowsFolder = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.Windows); +#endif + + if (directory.StartsWith(windowsFolder, StringComparison.OrdinalIgnoreCase) || (directory.StartsWith(FrameworkLocationHelper.programFiles32, StringComparison.OrdinalIgnoreCase)) || (!String.IsNullOrEmpty(FrameworkLocationHelper.programFiles64) && directory.StartsWith(FrameworkLocationHelper.programFiles64, StringComparison.OrdinalIgnoreCase))) { diff --git a/src/XMakeBuildEngine/Evaluation/ExpressionShredder.cs b/src/XMakeBuildEngine/Evaluation/ExpressionShredder.cs index 14bae367684..9aa1e0b6276 100644 --- a/src/XMakeBuildEngine/Evaluation/ExpressionShredder.cs +++ b/src/XMakeBuildEngine/Evaluation/ExpressionShredder.cs @@ -7,7 +7,9 @@ using System; using System.Collections; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using System.Diagnostics; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index 8273d9f308c..2032037e76e 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -25,6 +25,9 @@ + + SharedUtilities\FileUtilities.GetFolderPath.cs + true diff --git a/src/XMakeBuildEngine/Utilities/Utilities.cs b/src/XMakeBuildEngine/Utilities/Utilities.cs index cc844a16b49..f6333ddd8e4 100644 --- a/src/XMakeBuildEngine/Utilities/Utilities.cs +++ b/src/XMakeBuildEngine/Utilities/Utilities.cs @@ -511,12 +511,20 @@ internal static PropertyDictionary GetEnvironmentProper if (String.IsNullOrEmpty(localAppData)) { +#if FEATURE_SPECIAL_FOLDERS localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); +#else + localAppData = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.LocalApplicationData); +#endif } if (String.IsNullOrEmpty(localAppData)) { +#if FEATURE_SPECIAL_FOLDERS localAppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); +#else + localAppData = FileUtilities.GetFolderPath(FileUtilities.SpecialFolder.ApplicationData); +#endif } environmentProperties.Set(ProjectPropertyInstance.Create(ReservedPropertyNames.localAppData, localAppData)); diff --git a/src/XMakeBuildEngine/project.json b/src/XMakeBuildEngine/project.json index 4db95b78225..7bc1b3b95fe 100644 --- a/src/XMakeBuildEngine/project.json +++ b/src/XMakeBuildEngine/project.json @@ -13,7 +13,8 @@ }, "net46": { "dependencies": { - "Microsoft.Tpl.Dataflow": "4.5.24" + "Microsoft.Tpl.Dataflow": "4.5.24", + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23213" } }, "dnxcore50": { @@ -25,6 +26,7 @@ "System.Diagnostics.Process": "4.0.0-beta-23109", "System.Diagnostics.TraceSource": "4.0.0-beta-23019", "System.IO.Pipes": "4.0.0-beta-23123", + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23213", "System.Threading.Thread": "4.0.0-beta-23123", "System.Threading.ThreadPool": "4.0.10-beta-23123", "Microsoft.NETCore.Runtime": "1.0.0", diff --git a/src/XMakeBuildEngine/project.lock.json b/src/XMakeBuildEngine/project.lock.json index 0f30b2db01b..a8cf0332bd6 100644 --- a/src/XMakeBuildEngine/project.lock.json +++ b/src/XMakeBuildEngine/project.lock.json @@ -20,6 +20,34 @@ "runtime": { "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.dll": {} } + }, + "System.Resources.ResourceManager/4.0.0": { + "compile": { + "ref/net45/_._": {} + }, + "runtime": { + "lib/net45/_._": {} + } + }, + "System.Runtime/4.0.20": { + "compile": { + "ref/net46/_._": {} + }, + "runtime": { + "lib/net46/_._": {} + } + }, + "System.Runtime.InteropServices.RuntimeInformation/4.0.0-beta-23213": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Resources.ResourceManager": "[4.0.0, )" + }, + "compile": { + "ref/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtime": { + "lib/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + } } }, "DNXCore,Version=v5.0": { @@ -990,6 +1018,18 @@ "lib/DNXCore50/System.Runtime.InteropServices.dll": {} } }, + "System.Runtime.InteropServices.RuntimeInformation/4.0.0-beta-23213": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Resources.ResourceManager": "[4.0.0, )" + }, + "compile": { + "ref/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtime": { + "lib/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + } + }, "System.Runtime.Numerics/4.0.0": { "dependencies": { "System.Runtime": "[4.0.20, )", @@ -1022,13 +1062,13 @@ "lib/dotnet/System.Security.Claims.dll": {} } }, - "System.Security.Cryptography.Encryption/4.0.0-beta-23123": { + "System.Security.Cryptography.Encryption/4.0.0-beta-23109": { "dependencies": { - "System.Runtime": "[4.0.0, )", - "System.Resources.ResourceManager": "[4.0.0, )", - "System.IO": "[4.0.0, )", - "System.Threading.Tasks": "[4.0.0, )", - "System.Globalization": "[4.0.0, )" + "System.Runtime": "[4.0.0-beta-23109, )", + "System.Resources.ResourceManager": "[4.0.0-beta-23109, )", + "System.IO": "[4.0.0-beta-23109, )", + "System.Threading.Tasks": "[4.0.0-beta-23109, )", + "System.Globalization": "[4.0.0-beta-23109, )" }, "compile": { "ref/dotnet/System.Security.Cryptography.Encryption.dll": {} @@ -1048,14 +1088,14 @@ "lib/dotnet/System.Security.Principal.dll": {} } }, - "System.Security.SecureString/4.0.0-beta-23123": { + "System.Security.SecureString/4.0.0-beta-23109": { "dependencies": { - "System.Runtime": "[4.0.20, )", - "System.Runtime.InteropServices": "[4.0.20, )", - "System.Resources.ResourceManager": "[4.0.0, )", - "System.Runtime.Handles": "[4.0.0, )", - "System.Security.Cryptography.Encryption": "[4.0.0-beta-23123, )", - "System.Threading": "[4.0.10, )" + "System.Runtime": "[4.0.20-beta-23109, )", + "System.Runtime.InteropServices": "[4.0.20-beta-23109, )", + "System.Resources.ResourceManager": "[4.0.0-beta-23109, )", + "System.Runtime.Handles": "[4.0.0-beta-23109, )", + "System.Security.Cryptography.Encryption": "[4.0.0-beta-23109, )", + "System.Threading": "[4.0.10-beta-23109, )" }, "compile": { "ref/dotnet/System.Security.SecureString.dll": {} @@ -1275,6 +1315,34 @@ "runtime": { "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.dll": {} } + }, + "System.Resources.ResourceManager/4.0.0": { + "compile": { + "ref/net45/_._": {} + }, + "runtime": { + "lib/net45/_._": {} + } + }, + "System.Runtime/4.0.20": { + "compile": { + "ref/net46/_._": {} + }, + "runtime": { + "lib/net46/_._": {} + } + }, + "System.Runtime.InteropServices.RuntimeInformation/4.0.0-beta-23213": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Resources.ResourceManager": "[4.0.0, )" + }, + "compile": { + "ref/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtime": { + "lib/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + } } }, "DNXCore,Version=v5.0/win7-x86": { @@ -2463,6 +2531,18 @@ "lib/DNXCore50/System.Runtime.InteropServices.dll": {} } }, + "System.Runtime.InteropServices.RuntimeInformation/4.0.0-beta-23213": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Resources.ResourceManager": "[4.0.0, )" + }, + "compile": { + "ref/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtime": { + "lib/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll": {} + } + }, "System.Runtime.Numerics/4.0.0": { "dependencies": { "System.Runtime": "[4.0.20, )", @@ -4964,6 +5044,26 @@ "[Content_Types].xml" ] }, + "System.Runtime.InteropServices.RuntimeInformation/4.0.0-beta-23213": { + "sha512": "yzVJM7dF6XqnGTkv2IRufKs8AiqDpfdfBvMT5sVgY2fCtUXdkcjxiIWzaVIau8IYrJUlQDmSpeQ8NV6l1vz0Lg==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Runtime.InteropServices.RuntimeInformation.nuspec", + "lib/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "ref/dotnet/System.Runtime.InteropServices.RuntimeInformation.dll", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "package/services/metadata/core-properties/979d708fec824c778c40c2377d8978ca.psmdcp", + "[Content_Types].xml" + ] + }, "System.Runtime.Numerics/4.0.0": { "sha512": "aAYGEOE01nabQLufQ4YO8WuSyZzOqGcksi8m1BRW8ppkmssR7en8TqiXcBkB2gTkCnKG/Ai2NQY8CgdmgZw/fw==", "type": "Package", @@ -5027,6 +5127,30 @@ "[Content_Types].xml" ] }, + "System.Security.Cryptography.Encryption/4.0.0-beta-23109": { + "sha512": "Lewf25aZTItYvFz8RtCFdNAniNQ737gkTPU9IObuzbOeX8MMGHRf9Le4AQ2SdkTTTljjhebGybF5KCKZ784DLA==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Security.Cryptography.Encryption.nuspec", + "lib/DNXCore50/System.Security.Cryptography.Encryption.dll", + "lib/net46/System.Security.Cryptography.Encryption.dll", + "ref/dotnet/System.Security.Cryptography.Encryption.dll", + "ref/dotnet/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/zh-hant/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/de/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/fr/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/it/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/ja/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/ko/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/ru/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/zh-hans/System.Security.Cryptography.Encryption.xml", + "ref/dotnet/es/System.Security.Cryptography.Encryption.xml", + "ref/net46/System.Security.Cryptography.Encryption.dll", + "package/services/metadata/core-properties/3ee4ba1c540743d4a7948e470b79c126.psmdcp", + "[Content_Types].xml" + ] + }, "System.Security.Cryptography.Encryption/4.0.0-beta-23123": { "sha512": "IjawUtwaF88Ao3xkigg2I6pVj/uevJvuYtDk2lKXD6gYp833yK7D3dyelGGq0wV/GJtmEp64YqXLi6gW69/JGg==", "type": "Package", @@ -5092,6 +5216,30 @@ "[Content_Types].xml" ] }, + "System.Security.SecureString/4.0.0-beta-23109": { + "sha512": "ZRmBb18JXF2sfEop6zESpMH+yCPnr3lPfB3o7pyU4hnoMPIzwGJq8T9pIWG0y1x+fB1RtsaXUXPf3T1ACR2T5w==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Security.SecureString.nuspec", + "lib/DNXCore50/System.Security.SecureString.dll", + "lib/net46/System.Security.SecureString.dll", + "ref/dotnet/System.Security.SecureString.dll", + "ref/dotnet/System.Security.SecureString.xml", + "ref/dotnet/zh-hant/System.Security.SecureString.xml", + "ref/dotnet/de/System.Security.SecureString.xml", + "ref/dotnet/fr/System.Security.SecureString.xml", + "ref/dotnet/it/System.Security.SecureString.xml", + "ref/dotnet/ja/System.Security.SecureString.xml", + "ref/dotnet/ko/System.Security.SecureString.xml", + "ref/dotnet/ru/System.Security.SecureString.xml", + "ref/dotnet/zh-hans/System.Security.SecureString.xml", + "ref/dotnet/es/System.Security.SecureString.xml", + "ref/net46/System.Security.SecureString.dll", + "package/services/metadata/core-properties/f529806a9be74a3cb090de647dfd9b55.psmdcp", + "[Content_Types].xml" + ] + }, "System.Security.SecureString/4.0.0-beta-23123": { "sha512": "T35YL/7zWBYOLJCcntF+bQgZBgOy5qc6oPn4GWL1phv0borJawTL60iwk4MO2ReYYSK89JmJ7/yqKahqYNw72g==", "type": "Package", @@ -5531,7 +5679,8 @@ "Microsoft.Tpl.Dataflow >= 4.5.24" ], ".NETFramework,Version=v4.6": [ - "Microsoft.Tpl.Dataflow >= 4.5.24" + "Microsoft.Tpl.Dataflow >= 4.5.24", + "System.Runtime.InteropServices.RuntimeInformation >= 4.0.0-beta-23213" ], "DNXCore,Version=v5.0": [ "Microsoft.NETCore >= 5.0.0", @@ -5541,6 +5690,7 @@ "System.Diagnostics.Process >= 4.0.0-beta-23109", "System.Diagnostics.TraceSource >= 4.0.0-beta-23019", "System.IO.Pipes >= 4.0.0-beta-23123", + "System.Runtime.InteropServices.RuntimeInformation >= 4.0.0-beta-23213", "System.Threading.Thread >= 4.0.0-beta-23123", "System.Threading.ThreadPool >= 4.0.10-beta-23123", "Microsoft.NETCore.Runtime >= 1.0.0", From 09a05334fbaaf486f319210edc5765bc173c665a Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 19 Aug 2015 18:23:41 -0700 Subject: [PATCH 3/9] Add reference to System.Diagnostics.Contracts, add #if's for security permissions --- .../Communications/NodeProviderOutOfProc.cs | 2 ++ .../Communications/NodeProviderOutOfProcBase.cs | 2 ++ .../Communications/NodeProviderOutOfProcTaskHost.cs | 2 ++ .../Collections/RetrievableEntryHashSet/HashSet.cs | 6 ++++-- .../Errors/InternalLoggerException.cs | 2 ++ .../Errors/InvalidProjectFileException.cs | 2 ++ .../Errors/InvalidToolsetDefinitionException.cs | 2 ++ src/XMakeBuildEngine/project.json | 1 + src/XMakeBuildEngine/project.lock.json | 12 ++++++++++++ 9 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs index 019bb73fb25..8b8956b48c8 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProc.cs @@ -17,7 +17,9 @@ using System.Security; using System.Security.AccessControl; using System.Security.Principal; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using Microsoft.Build.Shared; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index ecffbf608ac..5a4114887c3 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -18,7 +18,9 @@ using System.Security; using System.Security.AccessControl; using System.Security.Principal; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using Microsoft.Build.Shared; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index 4950f0f9def..6c8dd0434bd 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -18,7 +18,9 @@ using System.Security; using System.Security.AccessControl; using System.Security.Principal; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using Microsoft.Build.Shared; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/Collections/RetrievableEntryHashSet/HashSet.cs b/src/XMakeBuildEngine/Collections/RetrievableEntryHashSet/HashSet.cs index 4c1cc26c408..ff2cb35a6bc 100644 --- a/src/XMakeBuildEngine/Collections/RetrievableEntryHashSet/HashSet.cs +++ b/src/XMakeBuildEngine/Collections/RetrievableEntryHashSet/HashSet.cs @@ -9,7 +9,9 @@ #if !SILVERLIGHT using System.Runtime.Serialization; #endif +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using System.Text; using System.Diagnostics.CodeAnalysis; using System.Security; @@ -88,7 +90,7 @@ namespace Microsoft.Build.Collections public class HashSet : ICollection, ISet #else [Serializable()] -#if !MONO +#if !MONO && FEATURE_SECURITY_PERMISSIONS [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] #endif internal class RetrievableEntryHashSet : ICollection, @@ -1734,7 +1736,7 @@ internal struct Slot #if !SILVERLIGHT [Serializable()] -#if !MONO +#if !MONO && FEATURE_SECURITY_PERMISSIONS [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] #endif #endif diff --git a/src/XMakeBuildEngine/Errors/InternalLoggerException.cs b/src/XMakeBuildEngine/Errors/InternalLoggerException.cs index b0a2d5bd9c3..48f34534f12 100644 --- a/src/XMakeBuildEngine/Errors/InternalLoggerException.cs +++ b/src/XMakeBuildEngine/Errors/InternalLoggerException.cs @@ -3,7 +3,9 @@ using System; using System.Runtime.Serialization; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using Microsoft.Build.Shared; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/Errors/InvalidProjectFileException.cs b/src/XMakeBuildEngine/Errors/InvalidProjectFileException.cs index f739c7af6cf..104166691fd 100644 --- a/src/XMakeBuildEngine/Errors/InvalidProjectFileException.cs +++ b/src/XMakeBuildEngine/Errors/InvalidProjectFileException.cs @@ -5,7 +5,9 @@ using System.IO; using System.Xml; using System.Runtime.Serialization; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif using Microsoft.Build.Shared; using Microsoft.Build.Evaluation; diff --git a/src/XMakeBuildEngine/Errors/InvalidToolsetDefinitionException.cs b/src/XMakeBuildEngine/Errors/InvalidToolsetDefinitionException.cs index fb2d890f523..0f29580501d 100644 --- a/src/XMakeBuildEngine/Errors/InvalidToolsetDefinitionException.cs +++ b/src/XMakeBuildEngine/Errors/InvalidToolsetDefinitionException.cs @@ -6,7 +6,9 @@ using Microsoft.Build.Shared; using System.Runtime.Serialization; +#if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; +#endif namespace Microsoft.Build.Exceptions { diff --git a/src/XMakeBuildEngine/project.json b/src/XMakeBuildEngine/project.json index 7bc1b3b95fe..053615af849 100644 --- a/src/XMakeBuildEngine/project.json +++ b/src/XMakeBuildEngine/project.json @@ -22,6 +22,7 @@ "Microsoft.NETCore": "5.0.0", "Microsoft.NETCore.Portable.Compatibility": "1.0.0", "System.Console": "4.0.0-beta-23123", + "System.Diagnostics.Contracts": "4.0.0", "System.Diagnostics.FileVersionInfo": "4.0.0-beta-23109", "System.Diagnostics.Process": "4.0.0-beta-23109", "System.Diagnostics.TraceSource": "4.0.0-beta-23019", diff --git a/src/XMakeBuildEngine/project.lock.json b/src/XMakeBuildEngine/project.lock.json index a8cf0332bd6..2b0cbd0fcca 100644 --- a/src/XMakeBuildEngine/project.lock.json +++ b/src/XMakeBuildEngine/project.lock.json @@ -375,6 +375,17 @@ "lib/DNXCore50/System.Console.dll": {} } }, + "System.Diagnostics.Contracts/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.0, )" + }, + "compile": { + "ref/dotnet/System.Diagnostics.Contracts.dll": {} + }, + "runtime": { + "lib/DNXCore50/System.Diagnostics.Contracts.dll": {} + } + }, "System.Diagnostics.Debug/4.0.10": { "dependencies": { "System.Runtime": "[4.0.0, )" @@ -5686,6 +5697,7 @@ "Microsoft.NETCore >= 5.0.0", "Microsoft.NETCore.Portable.Compatibility >= 1.0.0", "System.Console >= 4.0.0-beta-23123", + "System.Diagnostics.Contracts >= 4.0.0", "System.Diagnostics.FileVersionInfo >= 4.0.0-beta-23109", "System.Diagnostics.Process >= 4.0.0-beta-23109", "System.Diagnostics.TraceSource >= 4.0.0-beta-23019", From e9049da9dfd28879a042862f7297c2fb3cbc5d2b Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 19 Aug 2015 18:31:54 -0700 Subject: [PATCH 4/9] Disable file tracker for .NET Core --- dir.props | 2 ++ .../BackEnd/Components/RequestBuilder/TaskBuilder.cs | 2 ++ .../BackEnd/Components/RequestBuilder/TaskHost.cs | 2 ++ src/XMakeBuildEngine/Microsoft.Build.csproj | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dir.props b/dir.props index e88059b8e8f..f476d3e823c 100644 --- a/dir.props +++ b/dir.props @@ -110,6 +110,8 @@ $(DefineConstants);FEATURE_BINARY_SERIALIZATION true $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION + $(DefineConstants);FEATURE_FILE_TRACKER + true $(DefineConstants);FEATURE_GAC $(DefineConstants);FEATURE_GET_COMMANDLINE $(DefineConstants);FEATURE_OSVERSION diff --git a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskBuilder.cs b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskBuilder.cs index 38f1ae1e396..c0e6e89df5a 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskBuilder.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskBuilder.cs @@ -775,7 +775,9 @@ private async Task ExecuteInstantiatedTask(ITaskExecutionHost ta } else { +#if FEATURE_FILE_TRACKER using (FullTracking.Track(taskLoggingContext.TargetLoggingContext.Target.Name, _taskNode.Name, _buildRequestEntry.ProjectRootDirectory, _buildRequestEntry.RequestConfiguration.Project.PropertiesToBuildWith)) +#endif { taskResult = taskExecutionHost.Execute(); } diff --git a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskHost.cs index 08305e77d49..bc46cfe3d4f 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -784,7 +784,9 @@ private async Task BuildProjectFilesInParallelAsync(string[] List> targetOutputsPerProject = null; +#if FEATURE_FILE_TRACKER using (FullTracking.Suspend()) +#endif { bool overallSuccess = true; diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index 2032037e76e..7c2557753b2 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -135,7 +135,7 @@ true - + @@ -589,7 +589,7 @@ SharedUtilities\NativeMethodsShared.cs true - + InprocTrackingNativeMethods.cs true From 8e69f4d7903f6de32ccf66dd087aa4ff1d5106a5 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Wed, 19 Aug 2015 19:14:58 -0700 Subject: [PATCH 5/9] Port InterningBinaryReader and other miscellaneous to .NET Core --- dir.props | 4 ++++ src/Shared/Compat/TypeFilter.cs | 6 ++++++ src/Shared/InterningBinaryReader.cs | 21 +++++++++++++++---- src/Shared/NativeMethodsShared.cs | 3 ++- src/Shared/TaskLoader.cs | 8 ++++++- .../Components/Logging/LoggingService.cs | 6 +++--- .../Errors/InternalLoggerException.cs | 2 ++ .../Evaluation/IntrinsicFunctions.cs | 6 ++++-- .../Logging/LoggerDescription.cs | 9 ++++++++ src/XMakeBuildEngine/Microsoft.Build.csproj | 7 +++++-- 10 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 src/Shared/Compat/TypeFilter.cs diff --git a/dir.props b/dir.props index f476d3e823c..bac40ec8601 100644 --- a/dir.props +++ b/dir.props @@ -114,8 +114,10 @@ true $(DefineConstants);FEATURE_GAC $(DefineConstants);FEATURE_GET_COMMANDLINE + $(DefineConstants);FEATURE_MEMORYSTREAM_GETBUFFER $(DefineConstants);FEATURE_OSVERSION $(DefineConstants);FEATURE_REFLECTION_EMIT_DEBUG_INFO + $(DefineConstants);FEATURE_REGISTRYHIVE_DYNDATA $(DefineConstants);FEATURE_RESOURCE_EXPOSURE $(DefineConstants);FEATURE_SECURITY_PERMISSIONS $(DefineConstants);FEATURE_SPECIAL_FOLDERS @@ -125,6 +127,8 @@ $(DefineConstants);FEATURE_THREAD_CULTURE $(DefineConstants);FEATURE_THREAD_PRIORITY $(DefineConstants);FEATURE_TYPE_INVOKEMEMBER + $(DefineConstants);FEATURE_TYPE_GETINTERFACE + true $(DefineConstants);FEATURE_VARIOUS_EXCEPTIONS $(DefineConstants);FEATURE_XAML_TYPES true diff --git a/src/Shared/Compat/TypeFilter.cs b/src/Shared/Compat/TypeFilter.cs new file mode 100644 index 00000000000..50d43dd76a5 --- /dev/null +++ b/src/Shared/Compat/TypeFilter.cs @@ -0,0 +1,6 @@ +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + public delegate bool TypeFilter(Type m, object filterCriteria); +} \ No newline at end of file diff --git a/src/Shared/InterningBinaryReader.cs b/src/Shared/InterningBinaryReader.cs index a5776c1b3fc..dcc894c9a3d 100644 --- a/src/Shared/InterningBinaryReader.cs +++ b/src/Shared/InterningBinaryReader.cs @@ -66,7 +66,7 @@ override public String ReadString() MemoryStream memoryStream = this.BaseStream as MemoryStream; int currPos = 0; - int n; + int n = 0; int stringLength; int readLength; int charsRead; @@ -90,13 +90,14 @@ override public String ReadString() { readLength = ((stringLength - currPos) > MaxCharsBuffer) ? MaxCharsBuffer : (stringLength - currPos); - byte[] rawBuffer; - int rawPosition; + byte[] rawBuffer = null; + int rawPosition = 0; if (memoryStream != null) { // Optimization: we can avoid reading into a byte buffer // and instead read directly from the memorystream's backing buffer +#if FEATURE_MEMORYSTREAM_GETBUFFER rawBuffer = memoryStream.GetBuffer(); rawPosition = (int)memoryStream.Position; int length = (int)memoryStream.Length; @@ -110,8 +111,20 @@ override public String ReadString() { ErrorUtilities.ThrowInternalError("From calculating based on the memorystream, about to read n = {0}. length = {1}, rawPosition = {2}, readLength = {3}, stringLength = {4}, currPos = {5}.", n, length, rawPosition, readLength, stringLength, currPos); } +#else + ArraySegment rawBufferSegment; + if (memoryStream.TryGetBuffer(out rawBufferSegment)) + { + rawBuffer = rawBufferSegment.Array; + rawPosition = rawBufferSegment.Offset + (int) memoryStream.Position; + + long maxReadLength = memoryStream.Length - memoryStream.Position; + n = readLength > maxReadLength ? (int) maxReadLength : readLength; + } +#endif } - else + + if (rawBuffer == null) { rawBuffer = _buffer.ByteBuffer; rawPosition = 0; diff --git a/src/Shared/NativeMethodsShared.cs b/src/Shared/NativeMethodsShared.cs index 16d4b9b43a2..a7683cecd37 100644 --- a/src/Shared/NativeMethodsShared.cs +++ b/src/Shared/NativeMethodsShared.cs @@ -12,6 +12,7 @@ using System.Text.RegularExpressions; using System.Threading; using Microsoft.Win32.SafeHandles; +using System.Reflection; namespace Microsoft.Build.Shared { @@ -422,7 +423,7 @@ internal static string FrameworkCurrentPath if (s_frameworkCurrentPath == null) { s_frameworkCurrentPath = - Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(string)).Location) + Path.GetDirectoryName(typeof(string).GetTypeInfo().Assembly.Location) ?? string.Empty; } diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index b3bbb54e341..6a44898959f 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -8,6 +8,7 @@ using System; using Microsoft.Build.Framework; using System.Reflection; +using System.Linq; namespace Microsoft.Build.Shared { @@ -34,7 +35,12 @@ internal static class TaskLoader /// true, if specified type is a task internal static bool IsTaskClass(Type type, object unused) { - return (type.GetTypeInfo().IsClass && !type.GetTypeInfo().IsAbstract && (type.GetTypeInfo().GetInterface("Microsoft.Build.Framework.ITask") != null)); + return (type.GetTypeInfo().IsClass && !type.GetTypeInfo().IsAbstract && ( +#if FEATURE_TYPE_GETINTERFACE + type.GetTypeInfo().GetInterface("Microsoft.Build.Framework.ITask") != null)); +#else + type.GetInterfaces().Any(interfaceType => interfaceType.FullName == "Microsoft.Build.Framework.ITask"))); +#endif } /// diff --git a/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs b/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs index e3d1b534104..9284dd7e8eb 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs @@ -92,7 +92,7 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler, IBu /// We use a BindingFlags.Public flag here because the getter is public, so although the setter is internal, /// it is only discoverable with Reflection using the Public flag (go figure!) /// - private static Lazy s_projectStartedEventArgsGlobalProperties = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("GlobalProperties", BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty), LazyThreadSafetyMode.PublicationOnly); + private static Lazy s_projectStartedEventArgsGlobalProperties = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("GlobalProperties", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); /// /// A cached reflection accessor for an internal member. @@ -101,7 +101,7 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler, IBu /// We use a BindingFlags.Public flag here because the getter is public, so although the setter is internal, /// it is only discoverable with Reflection using the Public flag (go figure!) /// - private static Lazy s_projectStartedEventArgsToolsVersion = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("ToolsVersion", BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty), LazyThreadSafetyMode.PublicationOnly); + private static Lazy s_projectStartedEventArgsToolsVersion = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("ToolsVersion", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); #region Data @@ -696,7 +696,7 @@ public bool RegisterLogger(ILogger logger) if (_centralLoggerSinkId == -1) { // Create a forwarding logger which forwards all events to an eventSourceSink - Assembly engineAssembly = Assembly.GetAssembly(typeof(LoggingService)); + Assembly engineAssembly = typeof(LoggingService).GetTypeInfo().Assembly; string loggerClassName = "Microsoft.Build.BackEnd.Logging.CentralForwardingLogger"; string loggerAssemblyName = engineAssembly.GetName().FullName; LoggerDescription centralLoggerDescrption = new LoggerDescription diff --git a/src/XMakeBuildEngine/Errors/InternalLoggerException.cs b/src/XMakeBuildEngine/Errors/InternalLoggerException.cs index 48f34534f12..84c6daba754 100644 --- a/src/XMakeBuildEngine/Errors/InternalLoggerException.cs +++ b/src/XMakeBuildEngine/Errors/InternalLoggerException.cs @@ -240,7 +240,9 @@ internal static void Throw private string _helpKeyword; // This flag is set to indicate that the exception occured during logger initialization +#if FEATURE_BINARY_SERIALIZATION [OptionalField(VersionAdded = 2)] +#endif private bool _initializationException; } } diff --git a/src/XMakeBuildEngine/Evaluation/IntrinsicFunctions.cs b/src/XMakeBuildEngine/Evaluation/IntrinsicFunctions.cs index 51d0912d470..d2ec682e843 100644 --- a/src/XMakeBuildEngine/Evaluation/IntrinsicFunctions.cs +++ b/src/XMakeBuildEngine/Evaluation/IntrinsicFunctions.cs @@ -392,11 +392,11 @@ private static RegistryKey GetBaseKeyFromKeyName(string keyName, RegistryView vi int i = keyName.IndexOf('\\'); if (i != -1) { - basekeyName = keyName.Substring(0, i).ToUpper(System.Globalization.CultureInfo.InvariantCulture); + basekeyName = keyName.Substring(0, i).ToUpperInvariant(); } else { - basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture); + basekeyName = keyName.ToUpperInvariant(); } RegistryKey basekey = null; @@ -421,9 +421,11 @@ private static RegistryKey GetBaseKeyFromKeyName(string keyName, RegistryView vi case "HKEY_CURRENT_CONFIG": basekey = RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, view); break; +#if FEATURE_REGISTRYHIVE_DYNDATA case "HKEY_DYN_DATA": basekey = RegistryKey.OpenBaseKey(RegistryHive.DynData, view); break; +#endif default: ErrorUtilities.ThrowArgument(keyName); break; diff --git a/src/XMakeBuildEngine/Logging/LoggerDescription.cs b/src/XMakeBuildEngine/Logging/LoggerDescription.cs index dfdff897199..a176c260a40 100644 --- a/src/XMakeBuildEngine/Logging/LoggerDescription.cs +++ b/src/XMakeBuildEngine/Logging/LoggerDescription.cs @@ -11,6 +11,7 @@ using Microsoft.Build.BackEnd; using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; +using System.Linq; namespace Microsoft.Build.Logging { @@ -252,7 +253,11 @@ private static bool IsForwardingLoggerClass(Type type, object unused) { return (type.GetTypeInfo().IsClass && !type.GetTypeInfo().IsAbstract && +#if FEATURE_TYPE_GETINTERFACE (type.GetTypeInfo().GetInterface("IForwardingLogger") != null)); +#else + (type.GetInterfaces().Any(interfaceType => interfaceType.Name == "IForwardingLogger"))); +#endif } /// @@ -264,7 +269,11 @@ private static bool IsLoggerClass(Type type, object unused) { return (type.GetTypeInfo().IsClass && !type.GetTypeInfo().IsAbstract && +#if FEATURE_TYPE_GETINTERFACE (type.GetTypeInfo().GetInterface("ILogger") != null)); +#else + (type.GetInterfaces().Any(interfaceType => interfaceType.Name == "ILogger"))); +#endif } /// diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index 7c2557753b2..5f55bbe2b14 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -25,7 +25,10 @@ - + + SharedUtilities\Compat\TypeFilter.cs + + SharedUtilities\FileUtilities.GetFolderPath.cs @@ -135,7 +138,7 @@ true - + From 7a3bc66ea2888152fdc5f4e1bc37cbb79b5d4403 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 20 Aug 2015 14:26:40 -0700 Subject: [PATCH 6/9] Port LogMessagePacketBase and NativeMethodsShared to .NET Core --- dir.props | 4 ++ .../SafeHandleZeroOrMinusOneIsInvalid.cs | 18 +++++++ src/Shared/FileUtilities.cs | 8 ++- src/Shared/LogMessagePacketBase.cs | 22 ++++++-- src/Shared/NativeMethodsShared.cs | 50 +++++++++++++++---- src/XMakeBuildEngine/Microsoft.Build.csproj | 3 ++ 6 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 src/Shared/Compat/SafeHandleZeroOrMinusOneIsInvalid.cs diff --git a/dir.props b/dir.props index bac40ec8601..d9717d5c37e 100644 --- a/dir.props +++ b/dir.props @@ -110,10 +110,14 @@ $(DefineConstants);FEATURE_BINARY_SERIALIZATION true $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION + $(DefineConstants);FEATURE_CHARSET_AUTO + $(DefineConstants);FEATURE_DOTNETVERSION $(DefineConstants);FEATURE_FILE_TRACKER true $(DefineConstants);FEATURE_GAC $(DefineConstants);FEATURE_GET_COMMANDLINE + $(DefineConstants);FEATURE_HANDLE_SAFEWAITHANDLE + $(DefineConstants);FEATURE_HANDLEREF $(DefineConstants);FEATURE_MEMORYSTREAM_GETBUFFER $(DefineConstants);FEATURE_OSVERSION $(DefineConstants);FEATURE_REFLECTION_EMIT_DEBUG_INFO diff --git a/src/Shared/Compat/SafeHandleZeroOrMinusOneIsInvalid.cs b/src/Shared/Compat/SafeHandleZeroOrMinusOneIsInvalid.cs new file mode 100644 index 00000000000..30fdf403c31 --- /dev/null +++ b/src/Shared/Compat/SafeHandleZeroOrMinusOneIsInvalid.cs @@ -0,0 +1,18 @@ +namespace Microsoft.Win32.SafeHandles +{ + using System; + using System.Runtime.InteropServices; + + public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle + { + protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid + { + get + { return handle == IntPtr.Zero || handle == new IntPtr(-1); } + } + } +} \ No newline at end of file diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index 823ab528c02..245887a619d 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -624,7 +624,13 @@ internal static string CurrentExecutablePath if (NativeMethodsShared.IsWindows) { StringBuilder sb = new StringBuilder(NativeMethodsShared.MAX_PATH); - if (NativeMethodsShared.GetModuleFileName(NativeMethodsShared.NullHandleRef, sb, sb.Capacity) == 0) + if (NativeMethodsShared.GetModuleFileName( +#if FEATURE_HANDLEREF + NativeMethodsShared.NullHandleRef, +#else + IntPtr.Zero, +#endif + sb, sb.Capacity) == 0) { throw new System.ComponentModel.Win32Exception(); } diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 35ffbbc2be2..83bf012b8a6 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -105,10 +105,21 @@ internal enum LoggingEventType : int /// internal abstract class LogMessagePacketBase : INodePacket { +#if FEATURE_DOTNETVERSION /// /// The packet version, which is based on the CLR version. Cached because querying Environment.Version each time becomes an allocation bottleneck. /// private static readonly int s_defaultPacketVersion = (Environment.Version.Major * 10) + Environment.Version.Minor; +#else + private static readonly int s_defaultPacketVersion = GetDefaultPacketVersion(); + + private static int GetDefaultPacketVersion() + { + Assembly coreAssembly = typeof(object).GetTypeInfo().Assembly; + Version coreAssemblyVersion = coreAssembly.GetName().Version; + return 1000 + coreAssemblyVersion.Major * 10 + coreAssemblyVersion.Minor; + } +#endif /// /// Dictionary of methods used to read BuildEventArgs. @@ -271,7 +282,7 @@ internal void WriteToStream(INodePacketTranslator translator) if (!s_writeMethodCache.TryGetValue(_eventType, out methodInfo)) { Type eventDerivedType = _buildEvent.GetType(); - methodInfo = eventDerivedType.GetMethod("WriteToStream", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod); + methodInfo = eventDerivedType.GetMethod("WriteToStream", BindingFlags.NonPublic | BindingFlags.Instance); s_writeMethodCache.Add(_eventType, methodInfo); } } @@ -302,8 +313,13 @@ internal void WriteToStream(INodePacketTranslator translator) } else { +#if FEATURE_ASSEMBLY_LOCATION string assemblyLocation = _buildEvent.GetType().GetTypeInfo().Assembly.Location; translator.Translate(ref assemblyLocation); +#else + string assemblyName = _buildEvent.GetType().GetTypeInfo().Assembly.FullName; + translator.Translate(ref assemblyName); +#endif translator.TranslateDotNet(ref _buildEvent); } } @@ -333,7 +349,7 @@ internal void ReadFromStream(INodePacketTranslator translator) if (!s_readMethodCache.TryGetValue(_eventType, out methodInfo)) { Type eventDerivedType = _buildEvent.GetType(); - methodInfo = eventDerivedType.GetMethod("CreateFromStream", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod); + methodInfo = eventDerivedType.GetMethod("CreateFromStream", BindingFlags.NonPublic | BindingFlags.Instance); s_readMethodCache.Add(_eventType, methodInfo); } } @@ -413,7 +429,7 @@ private static Delegate CreateDelegateRobust(Type type, Object firstArgument, Me { try { - delegateMethod = Delegate.CreateDelegate(type, firstArgument, methodInfo); + delegateMethod = methodInfo.CreateDelegate(type, firstArgument); } catch (FileLoadException) { diff --git a/src/Shared/NativeMethodsShared.cs b/src/Shared/NativeMethodsShared.cs index a7683cecd37..851590ed83e 100644 --- a/src/Shared/NativeMethodsShared.cs +++ b/src/Shared/NativeMethodsShared.cs @@ -43,7 +43,9 @@ internal static class NativeMethodsShared private const string kernel32Dll = "kernel32.dll"; private const string mscoreeDLL = "mscoree.dll"; +#if FEATURE_HANDLEREF internal static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); +#endif internal static IntPtr NullIntPtr = new IntPtr(0); @@ -58,6 +60,12 @@ internal static class NativeMethodsShared internal const uint WAIT_OBJECT_0 = 0x00000000; internal const uint WAIT_TIMEOUT = 0x00000102; +#if FEATURE_CHARSET_AUTO + internal const CharSet AutoOrUnicode = CharSet.Auto; +#else + internal const CharSet AutoOrUnicode = CharSet.Unicode; +#endif + #endregion #region Enums @@ -188,7 +196,7 @@ protected override bool ReleaseHandle() /// /// Contains information about the current state of both physical and virtual memory, including extended memory /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + [StructLayout(LayoutKind.Sequential, CharSet = AutoOrUnicode)] internal class MemoryStatus { /// @@ -422,9 +430,16 @@ internal static string FrameworkCurrentPath { if (s_frameworkCurrentPath == null) { +#if FEATURE_ASSEMBLY_LOCATION s_frameworkCurrentPath = Path.GetDirectoryName(typeof(string).GetTypeInfo().Assembly.Location) ?? string.Empty; +#else + // There isn't really a concept of a framework path on .NET Core. Try using the app folder + s_frameworkCurrentPath = + Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + ?? string.Empty; +#endif } return s_frameworkCurrentPath; @@ -517,13 +532,15 @@ internal static string FixFrameworkPath(string path) internal static int SetErrorMode(int newMode) { - if (Environment.OSVersion.Version >= s_threadErrorModeMinOsVersion) +#if FEATURE_OSVERSION + if (Environment.OSVersion.Version < s_threadErrorModeMinOsVersion) { - int num; - SetErrorMode_Win7AndNewer(newMode, out num); - return num; + return SetErrorMode_VistaAndOlder(newMode); } - return SetErrorMode_VistaAndOlder(newMode); +#endif + int num; + SetErrorMode_Win7AndNewer(newMode, out num); + return num; } [DllImport("kernel32.dll", EntryPoint = "SetThreadErrorMode", SetLastError = true)] @@ -1054,7 +1071,13 @@ int[] filePart /// [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] [DllImport(kernel32Dll, SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern int GetModuleFileName(HandleRef hModule, [Out] StringBuilder buffer, int length); + internal static extern int GetModuleFileName( +#if FEATURE_HANDLEREF + HandleRef hModule, +#else + IntPtr hModule, +#endif + [Out] StringBuilder buffer, int length); [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] [DllImport("kernel32.dll")] @@ -1107,7 +1130,7 @@ internal static bool SetCurrentDirectory(string path) [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] [return: MarshalAs(UnmanagedType.Bool)] - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)] private static extern bool GlobalMemoryStatusEx([In, Out] MemoryStatus lpBuffer); [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] @@ -1119,11 +1142,11 @@ internal static bool SetCurrentDirectory(string path) internal static extern int GetLongPathName([In] string path, [Out] System.Text.StringBuilder fullpath, [In] int length); [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)] internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SecurityAttributes lpPipeAttributes, int nSize); [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")] - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)] internal static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped); /// @@ -1169,7 +1192,12 @@ internal static bool MsgWaitOne(this WaitHandle handle, int timeout) // VS needs this in order to allow the in-proc compilers to properly initialize, since they will make calls from the // build thread which the main thread (blocked on BuildSubmission.Execute) must service. int waitIndex; - int returnValue = CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_NONE, timeout, 1, new IntPtr[] { handle.SafeWaitHandle.DangerousGetHandle() }, out waitIndex); +#if FEATURE_HANDLE_SAFEWAITHANDLE + IntPtr handlePtr = handle.SafeWaitHandle.DangerousGetHandle(); +#else + IntPtr handlePtr = handle.GetSafeWaitHandle().DangerousGetHandle(); +#endif + int returnValue = CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_NONE, timeout, 1, new IntPtr[] { handlePtr }, out waitIndex); ErrorUtilities.VerifyThrow(returnValue == 0 || ((uint)returnValue == RPC_S_CALLPENDING && timeout != Timeout.Infinite), "Received {0} from CoWaitForMultipleHandles, but expected 0 (S_OK)", returnValue); return returnValue == 0; } diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index 5f55bbe2b14..4a1a8f87412 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -25,6 +25,9 @@ + + SharedUtilities\Compat\SafeHandleZeroOrMinusOneIsInvalid.cs + SharedUtilities\Compat\TypeFilter.cs From 3661350f5726098aef14c0046da2624423f52145 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 20 Aug 2015 15:34:11 -0700 Subject: [PATCH 7/9] Partially port NodeEndpointOutOfProcBase to .NET Core, disable TranslateDotNet in NodePacketTranslator --- dir.props | 1 + src/Shared/INodePacketTranslator.cs | 2 + src/Shared/LogMessagePacketBase.cs | 4 ++ src/Shared/NodeEndpointOutOfProcBase.cs | 47 +++++++++++++++++-- src/Shared/NodePacketTranslator.cs | 6 +++ src/Shared/NodeShutdown.cs | 2 + src/Shared/TaskHostConfiguration.cs | 2 + src/Shared/TaskHostTaskComplete.cs | 2 + src/Shared/TaskParameter.cs | 4 ++ .../Communications/NodeEndpointInProc.cs | 2 +- .../BackEnd/Node/NodeConfiguration.cs | 2 + .../BackEnd/Shared/BuildResult.cs | 2 + .../BackEnd/Shared/WorkUnitResult.cs | 2 + 13 files changed, 74 insertions(+), 4 deletions(-) diff --git a/dir.props b/dir.props index d9717d5c37e..87033eb66e1 100644 --- a/dir.props +++ b/dir.props @@ -101,6 +101,7 @@ $(DefineConstants);FEATURE_APARTMENT_STATE + $(DefineConstants);FEATURE_APM $(DefineConstants);FEATURE_APPDOMAIN $(DefineConstants);FEATURE_APPDOMAIN_UNHANDLED_EXCEPTION $(DefineConstants);FEATURE_ASSEMBLY_LOADFROM diff --git a/src/Shared/INodePacketTranslator.cs b/src/Shared/INodePacketTranslator.cs index d74a549d02c..888946e51db 100644 --- a/src/Shared/INodePacketTranslator.cs +++ b/src/Shared/INodePacketTranslator.cs @@ -192,6 +192,7 @@ BinaryWriter Writer /// works in all of our current cases, but certainly isn't perfectly generic. void TranslateEnum(ref T value, int numericValue); +#if FEATURE_BINARY_SERIALIZATION /// /// Translates a value using the .Net binary formatter. /// @@ -203,6 +204,7 @@ BinaryWriter Writer /// methods. /// void TranslateDotNet(ref T value); +#endif /// /// Translates an object implementing INodePacketTranslatable. diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 83bf012b8a6..0b6107eac40 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -320,7 +320,9 @@ internal void WriteToStream(INodePacketTranslator translator) string assemblyName = _buildEvent.GetType().GetTypeInfo().Assembly.FullName; translator.Translate(ref assemblyName); #endif +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _buildEvent); +#endif } } @@ -396,7 +398,9 @@ internal void ReadFromStream(INodePacketTranslator translator) try { +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _buildEvent); +#endif } finally { diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index b3d54635c64..7f713065e1c 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -19,6 +19,7 @@ using System.Security; using System.Security.AccessControl; using System.Security.Principal; +using System.Threading.Tasks; #if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; #endif @@ -246,7 +247,7 @@ private void RaiseLinkStatusChanged(LinkStatus newStatus) { if (null != OnLinkStatusChanged) { - LinkStatusChangedDelegate linkStatusDelegate = (LinkStatusChangedDelegate)OnLinkStatusChanged.Clone(); + LinkStatusChangedDelegate linkStatusDelegate = OnLinkStatusChanged; linkStatusDelegate(this, newStatus); } } @@ -263,7 +264,7 @@ private void InternalDisconnect() _terminatePacketPump.Set(); _packetPump.Join(); _terminatePacketPump.Dispose(); - _pipeServer.Close(); + _pipeServer.Dispose(); _packetPump = null; ChangeLinkStatus(LinkStatus.Inactive); } @@ -330,10 +331,18 @@ private void PacketPumpProc() try { // Wait for a connection +#if FEATURE_APM IAsyncResult resultForConnection = localPipeServer.BeginWaitForConnection(null, null); +#else + Task connectionTask = localPipeServer.WaitForConnectionAsync(); +#endif CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); +#if FEATURE_APM bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false); +#else + bool connected = connectionTask.Wait(waitTimeRemaining); +#endif if (!connected) { CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us. Exiting comm thread."); @@ -342,7 +351,9 @@ private void PacketPumpProc() } CommunicationsUtilities.Trace("Parent started connecting. Reading handshake from parent"); +#if FEATURE_APM localPipeServer.EndWaitForConnection(resultForConnection); +#endif // The handshake protocol is a simple long exchange. The host sends us a long, and we // respond with another long. Once the handshake is complete, both sides can be assured the @@ -434,14 +445,24 @@ private void PacketPumpProc() // spammed to the endpoint and it never gets an opportunity to shutdown. CommunicationsUtilities.Trace("Entering read loop."); byte[] headerByte = new byte[5]; +#if FEATURE_APM IAsyncResult result = localPipeServer.BeginRead(headerByte, 0, headerByte.Length, null, null); +#else + Task readTask = localPipeServer.ReadAsync(headerByte, 0, headerByte.Length); +#endif bool exitLoop = false; do { // Ordering is important. We want packetAvailable to supercede terminate otherwise we will not properly wait for all // packets to be sent by other threads which are shutting down, such as the logging thread. - WaitHandle[] handles = new WaitHandle[] { result.AsyncWaitHandle, localPacketAvailable, localTerminatePacketPump }; + WaitHandle[] handles = new WaitHandle[] { +#if FEATURE_APM + result.AsyncWaitHandle, +#else + ((IAsyncResult)readTask).AsyncWaitHandle, +#endif + localPacketAvailable, localTerminatePacketPump }; int waitId = WaitHandle.WaitAny(handles); switch (waitId) @@ -451,7 +472,11 @@ private void PacketPumpProc() int bytesRead = 0; try { +#if FEATURE_APM bytesRead = localPipeServer.EndRead(result); +#else + bytesRead = readTask.Result; +#endif } catch (Exception e) { @@ -497,7 +522,11 @@ private void PacketPumpProc() break; } +#if FEATURE_APM result = localPipeServer.BeginRead(headerByte, 0, headerByte.Length, null, null); +#else + readTask = localPipeServer.ReadAsync(headerByte, 0, headerByte.Length); +#endif } break; @@ -532,7 +561,19 @@ private void PacketPumpProc() packetStream.Position = 1; packetStream.Write(BitConverter.GetBytes((int)packetStream.Length - 5), 0, 4); +#if FEATURE_MEMORYSTREAM_GETBUFFER localPipeServer.Write(packetStream.GetBuffer(), 0, (int)packetStream.Length); +#else + ArraySegment packetStreamBuffer; + if (packetStream.TryGetBuffer(out packetStreamBuffer)) + { + localPipeServer.Write(packetStreamBuffer.Array, packetStreamBuffer.Offset, packetStreamBuffer.Count); + } + else + { + localPipeServer.Write(packetStream.ToArray(), 0, (int)packetStream.Length); + } +#endif packetCount--; } diff --git a/src/Shared/NodePacketTranslator.cs b/src/Shared/NodePacketTranslator.cs index 42da9e6eeb6..5876b15b67c 100644 --- a/src/Shared/NodePacketTranslator.cs +++ b/src/Shared/NodePacketTranslator.cs @@ -12,7 +12,9 @@ using System.Text; using System.IO; using System.Threading; +#if FEATURE_BINARY_SERIALIZATION using System.Runtime.Serialization.Formatters.Binary; +#endif using Microsoft.Build.Collections; using Microsoft.Build.Execution; using Microsoft.Build.Framework; @@ -323,6 +325,7 @@ public void TranslateEnum(ref T value, int numericValue) value = (T)Enum.ToObject(enumType, numericValue); } +#if FEATURE_BINARY_SERIALIZATION /// /// Translates a value using the .Net binary formatter. /// @@ -338,6 +341,7 @@ public void TranslateDotNet(ref T value) BinaryFormatter formatter = new BinaryFormatter(); value = (T)formatter.Deserialize(_packetStream); } +#endif /// /// Translates an object implementing INodePacketTranslatable. @@ -786,6 +790,7 @@ public void TranslateEnum(ref T value, int numericValue) _writer.Write(numericValue); } +#if FEATURE_BINARY_SERIALIZATION /// /// Translates a value using the .Net binary formatter. /// @@ -801,6 +806,7 @@ public void TranslateDotNet(ref T value) BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(_packetStream, value); } +#endif /// /// Translates an object implementing INodePacketTranslatable. diff --git a/src/Shared/NodeShutdown.cs b/src/Shared/NodeShutdown.cs index 745b2502458..971ada45f5c 100644 --- a/src/Shared/NodeShutdown.cs +++ b/src/Shared/NodeShutdown.cs @@ -110,7 +110,9 @@ public Exception Exception public void Translate(INodePacketTranslator translator) { translator.TranslateEnum(ref _reason, (int)_reason); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _exception); +#endif } /// diff --git a/src/Shared/TaskHostConfiguration.cs b/src/Shared/TaskHostConfiguration.cs index 64da053f9b7..1ab3294c8a7 100644 --- a/src/Shared/TaskHostConfiguration.cs +++ b/src/Shared/TaskHostConfiguration.cs @@ -318,7 +318,9 @@ public void Translate(INodePacketTranslator translator) translator.TranslateDictionary(ref _buildProcessEnvironment, StringComparer.OrdinalIgnoreCase); translator.TranslateCulture(ref _culture); translator.TranslateCulture(ref _uiCulture); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _appDomainSetup); +#endif translator.Translate(ref _lineNumberOfTask); translator.Translate(ref _columnNumberOfTask); translator.Translate(ref _projectFileOfTask); diff --git a/src/Shared/TaskHostTaskComplete.cs b/src/Shared/TaskHostTaskComplete.cs index 523c2d7f703..f89d113f2a3 100644 --- a/src/Shared/TaskHostTaskComplete.cs +++ b/src/Shared/TaskHostTaskComplete.cs @@ -215,7 +215,9 @@ public NodePacketType Type public void Translate(INodePacketTranslator translator) { translator.TranslateEnum(ref _taskResult, (int)_taskResult); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _taskException); +#endif translator.Translate(ref _taskExceptionMessage); translator.Translate(ref _taskExceptionMessageArgs); translator.TranslateDictionary(ref _taskOutputParameters, StringComparer.OrdinalIgnoreCase, TaskParameter.FactoryForDeserialization); diff --git a/src/Shared/TaskParameter.cs b/src/Shared/TaskParameter.cs index 02b361c8738..d0315a28a0a 100644 --- a/src/Shared/TaskParameter.cs +++ b/src/Shared/TaskParameter.cs @@ -228,21 +228,25 @@ public void Translate(INodePacketTranslator translator) translator.Translate(ref stringArrayParam); _wrappedParameter = stringArrayParam; break; +#if FEATURE_BINARY_SERIALIZATION case TaskParameterType.ValueType: case TaskParameterType.ValueTypeArray: translator.TranslateDotNet(ref _wrappedParameter); break; +#endif case TaskParameterType.ITaskItem: TranslateITaskItem(translator); break; case TaskParameterType.ITaskItemArray: TranslateITaskItemArray(translator); break; +#if FEATURE_BINARY_SERIALIZATION case TaskParameterType.Invalid: Exception exceptionParam = (Exception)_wrappedParameter; translator.TranslateDotNet(ref exceptionParam); _wrappedParameter = exceptionParam; break; +#endif default: ErrorUtilities.ThrowInternalErrorUnreachable(); break; diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeEndpointInProc.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeEndpointInProc.cs index 49d271f0fd3..d6968eeaf6e 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeEndpointInProc.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeEndpointInProc.cs @@ -257,7 +257,7 @@ private void RaiseLinkStatusChanged(LinkStatus newStatus) { if (null != OnLinkStatusChanged) { - LinkStatusChangedDelegate linkStatusDelegate = (LinkStatusChangedDelegate)OnLinkStatusChanged.Clone(); + LinkStatusChangedDelegate linkStatusDelegate = OnLinkStatusChanged; linkStatusDelegate(this, newStatus); } } diff --git a/src/XMakeBuildEngine/BackEnd/Node/NodeConfiguration.cs b/src/XMakeBuildEngine/BackEnd/Node/NodeConfiguration.cs index b9e4a2cbf1b..4abf13326d6 100644 --- a/src/XMakeBuildEngine/BackEnd/Node/NodeConfiguration.cs +++ b/src/XMakeBuildEngine/BackEnd/Node/NodeConfiguration.cs @@ -141,7 +141,9 @@ public void Translate(INodePacketTranslator translator) translator.Translate(ref _nodeId); translator.Translate(ref _buildParameters, BuildParameters.FactoryForDeserialization); translator.TranslateArray(ref _forwardingLoggers, LoggerDescription.FactoryForTranslation); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _appDomainSetup); +#endif } /// diff --git a/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs b/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs index 22004eca01f..9dbc11e3ac8 100644 --- a/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs +++ b/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs @@ -579,7 +579,9 @@ void INodePacketTranslatable.Translate(INodePacketTranslator translator) translator.Translate(ref _initialTargets); translator.Translate(ref _defaultTargets); translator.Translate(ref _circularDependency); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _requestException); +#endif translator.TranslateDictionary, TargetResult>(ref _resultsByTarget, TargetResult.FactoryForDeserialization, CreateTargetResultDictionary); translator.Translate(ref _baseOverallResult); translator.Translate(ref _projectStateAfterBuild, ProjectInstance.FactoryForDeserialization); diff --git a/src/XMakeBuildEngine/BackEnd/Shared/WorkUnitResult.cs b/src/XMakeBuildEngine/BackEnd/Shared/WorkUnitResult.cs index 04c2b2ce78d..0da76e9716b 100644 --- a/src/XMakeBuildEngine/BackEnd/Shared/WorkUnitResult.cs +++ b/src/XMakeBuildEngine/BackEnd/Shared/WorkUnitResult.cs @@ -134,7 +134,9 @@ public void Translate(INodePacketTranslator translator) { translator.TranslateEnum(ref _resultCode, (int)_resultCode); translator.TranslateEnum(ref _actionCode, (int)_actionCode); +#if FEATURE_BINARY_SERIALIZATION translator.TranslateDotNet(ref _exception); +#endif } #endregion From 306dde7e9fafa55b5c6abf98754fe81d76f9fcee Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Thu, 20 Aug 2015 17:16:48 -0700 Subject: [PATCH 8/9] Add XmlTextWriter and dependencies from .NET Framework reference source. Port to .NET Core. Other minor porting. --- dir.props | 6 +- src/Shared/Compat/Base64Encoder.cs | 125 ++ src/Shared/Compat/BinHexEncoder.cs | 79 + src/Shared/Compat/SecureStringHasher.cs | 47 + src/Shared/Compat/ValidateNames.cs | 682 ++++++++ src/Shared/Compat/XmlCharType.cs | 796 +++++++++ src/Shared/Compat/XmlReservedNs.cs | 42 + src/Shared/Compat/XmlTextEncoder.cs | 504 ++++++ src/Shared/Compat/XmlTextWriter.cs | 1484 +++++++++++++++++ .../NodePacketTranslatorExtensions.cs | 2 + .../Communications/NodeProviderInProc.cs | 12 +- .../NodeProviderOutOfProcBase.cs | 4 +- .../ParallelLogger/ParallelConsoleLogger.cs | 2 + src/XMakeBuildEngine/Microsoft.Build.csproj | 52 +- src/XMakeBuildEngine/project.json | 1 + src/XMakeBuildEngine/project.lock.json | 73 + 16 files changed, 3888 insertions(+), 23 deletions(-) create mode 100644 src/Shared/Compat/Base64Encoder.cs create mode 100644 src/Shared/Compat/BinHexEncoder.cs create mode 100644 src/Shared/Compat/SecureStringHasher.cs create mode 100644 src/Shared/Compat/ValidateNames.cs create mode 100644 src/Shared/Compat/XmlCharType.cs create mode 100644 src/Shared/Compat/XmlReservedNs.cs create mode 100644 src/Shared/Compat/XmlTextEncoder.cs create mode 100644 src/Shared/Compat/XmlTextWriter.cs diff --git a/dir.props b/dir.props index 87033eb66e1..9f55e473afe 100644 --- a/dir.props +++ b/dir.props @@ -109,7 +109,7 @@ $(DefineConstants);FEATURE_ASSEMBLYNAME_CULTUREINFO $(DefineConstants);FEATURE_ASSEMBLYNAME_CLONE $(DefineConstants);FEATURE_BINARY_SERIALIZATION - true + $(DefineConstants);FEATURE_CONSOLE_BUFFERWIDTH $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION $(DefineConstants);FEATURE_CHARSET_AUTO $(DefineConstants);FEATURE_DOTNETVERSION @@ -133,14 +133,10 @@ $(DefineConstants);FEATURE_THREAD_PRIORITY $(DefineConstants);FEATURE_TYPE_INVOKEMEMBER $(DefineConstants);FEATURE_TYPE_GETINTERFACE - true $(DefineConstants);FEATURE_VARIOUS_EXCEPTIONS $(DefineConstants);FEATURE_XAML_TYPES true $(DefineConstants);FEATURE_XML_SOURCE_URI - - - diff --git a/src/Shared/Compat/Base64Encoder.cs b/src/Shared/Compat/Base64Encoder.cs new file mode 100644 index 00000000000..5547ded7ef2 --- /dev/null +++ b/src/Shared/Compat/Base64Encoder.cs @@ -0,0 +1,125 @@ + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +using System.Text; +using System.Diagnostics; + +namespace System.Xml { + + internal abstract partial class Base64Encoder { + + byte[] leftOverBytes; + int leftOverBytesCount; + char[] charsLine; + + internal const int Base64LineSize = 76; + internal const int LineSizeInBytes = Base64LineSize/4*3; + + internal Base64Encoder() { + charsLine = new char[Base64LineSize]; + } + + internal abstract void WriteChars( char[] chars, int index, int count ); + + internal void Encode( byte[] buffer, int index, int count ) { + if ( buffer == null ) { + throw new ArgumentNullException( "buffer" ); + } + if ( index < 0 ) { + throw new ArgumentOutOfRangeException( "index" ); + } + if ( count < 0 ) { + throw new ArgumentOutOfRangeException( "count" ); + } + if ( count > buffer.Length - index ) { + throw new ArgumentOutOfRangeException( "count" ); + } + + // encode left-over buffer + if( leftOverBytesCount > 0 ) { + int i = leftOverBytesCount; + while ( i < 3 && count > 0 ) { + leftOverBytes[i++] = buffer[index++]; + count--; + } + + // the total number of buffer we have is less than 3 -> return + if ( count == 0 && i < 3 ) { + leftOverBytesCount = i; + return; + } + + // encode the left-over buffer and write out + int leftOverChars = Convert.ToBase64CharArray( leftOverBytes, 0, 3, charsLine, 0 ); + WriteChars( charsLine, 0, leftOverChars ); + } + + // store new left-over buffer + leftOverBytesCount = count % 3; + if ( leftOverBytesCount > 0 ) { + count -= leftOverBytesCount; + if ( leftOverBytes == null ) { + leftOverBytes = new byte[3]; + } + for( int i = 0; i < leftOverBytesCount; i++ ) { + leftOverBytes[i] = buffer[ index + count + i ]; + } + } + + // encode buffer in 76 character long chunks + int endIndex = index + count; + int chunkSize = LineSizeInBytes; + while( index < endIndex ) { + if ( index + chunkSize > endIndex ) { + chunkSize = endIndex - index; + } + int charCount = Convert.ToBase64CharArray( buffer, index, chunkSize, charsLine, 0 ); + WriteChars( charsLine, 0, charCount ); + + index += chunkSize; + } + } + + internal void Flush() { + if ( leftOverBytesCount > 0 ) { + int leftOverChars = Convert.ToBase64CharArray( leftOverBytes, 0, leftOverBytesCount, charsLine, 0 ); + WriteChars( charsLine, 0, leftOverChars ); + leftOverBytesCount = 0; + } + } + } + + //internal partial class XmlRawWriterBase64Encoder : Base64Encoder { + + // XmlRawWriter rawWriter; + + // internal XmlRawWriterBase64Encoder( XmlRawWriter rawWriter ) { + // this.rawWriter = rawWriter; + // } + + // internal override void WriteChars( char[] chars, int index, int count ) { + // rawWriter.WriteRaw( chars, index, count ); + // } + //} + +#if !SILVERLIGHT || FEATURE_NETCORE + internal partial class XmlTextWriterBase64Encoder : Base64Encoder { + + XmlTextEncoder xmlTextEncoder; + + internal XmlTextWriterBase64Encoder( XmlTextEncoder xmlTextEncoder ) { + this.xmlTextEncoder = xmlTextEncoder; + } + + internal override void WriteChars( char[] chars, int index, int count ) { + xmlTextEncoder.WriteRaw( chars, index, count ); + } + } +#endif + +} diff --git a/src/Shared/Compat/BinHexEncoder.cs b/src/Shared/Compat/BinHexEncoder.cs new file mode 100644 index 00000000000..84af530c3a0 --- /dev/null +++ b/src/Shared/Compat/BinHexEncoder.cs @@ -0,0 +1,79 @@ + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +namespace System.Xml { + internal static partial class BinHexEncoder { + + private const string s_hexDigits = "0123456789ABCDEF"; + private const int CharsChunkSize = 128; + + internal static void Encode( byte[] buffer, int index, int count, XmlWriter writer ) { + if ( buffer == null ) { + throw new ArgumentNullException( "buffer" ); + } + if ( index < 0 ) { + throw new ArgumentOutOfRangeException( "index" ); + } + if ( count < 0 ) { + throw new ArgumentOutOfRangeException( "count" ); + } + if ( count > buffer.Length - index ) { + throw new ArgumentOutOfRangeException( "count" ); + } + + char[] chars = new char[ ( count * 2 ) < CharsChunkSize ? ( count * 2 ) : CharsChunkSize ]; + int endIndex = index + count; + while ( index < endIndex ) { + int cnt = ( count < CharsChunkSize/2 ) ? count : CharsChunkSize/2; + int charCount = Encode( buffer, index, cnt, chars ); + writer.WriteRaw( chars, 0, charCount ); + index += cnt; + count -= cnt; + } + } + + internal static string Encode(byte[] inArray, int offsetIn, int count) { + if (null == inArray) { + throw new ArgumentNullException("inArray"); + } + if (0 > offsetIn) { + throw new ArgumentOutOfRangeException("offsetIn"); + } + if (0 > count) { + throw new ArgumentOutOfRangeException("count"); + } + if (count > inArray.Length - offsetIn) { + throw new ArgumentOutOfRangeException("count"); + } + + char[] outArray = new char[2 * count]; + int lenOut = Encode(inArray, offsetIn, count, outArray); + return new String(outArray, 0, lenOut); + } + + private static int Encode(byte[] inArray, int offsetIn, int count, char[] outArray) { + int curOffsetOut =0, offsetOut = 0; + byte b; + int lengthOut = outArray.Length; + + for (int j=0; j> 4]; + if (curOffsetOut == lengthOut) { + break; + } + outArray[curOffsetOut ++] = s_hexDigits[b & 0xF]; + if (curOffsetOut == lengthOut) { + break; + } + } + return curOffsetOut - offsetOut; + } // function + + } // class +} // namespace diff --git a/src/Shared/Compat/SecureStringHasher.cs b/src/Shared/Compat/SecureStringHasher.cs new file mode 100644 index 00000000000..05e248b64bd --- /dev/null +++ b/src/Shared/Compat/SecureStringHasher.cs @@ -0,0 +1,47 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; + +namespace System.Xml { + + // SecureStringHasher is a hash code provider for strings. The hash codes calculation starts with a seed (hasCodeRandomizer) which is usually + // different for each instance of SecureStringHasher. Since the hash code depend on the seed, the chance of hashtable DoS attack in case when + // someone passes in lots of strings that hash to the same hash code is greatly reduced. + // The SecureStringHasher implements IEqualityComparer for strings and therefore can be used in generic IDictionary. + internal class SecureStringHasher : IEqualityComparer { + int hashCodeRandomizer; + + public SecureStringHasher() { + this.hashCodeRandomizer = Environment.TickCount; + } + +#if false // This is here only for debugging of hashing issues + public SecureStringHasher( int hashCodeRandomizer ) { + this.hashCodeRandomizer = hashCodeRandomizer; + } +#endif + + public bool Equals( String x, String y ) { + return String.Equals( x, y, StringComparison.Ordinal ); + } + + public int GetHashCode( String key ) { + int hashCode = hashCodeRandomizer; + // use key.Length to eliminate the rangecheck + for ( int i = 0; i < key.Length; i++ ) { + hashCode += ( hashCode << 7 ) ^ key[i]; + } + // mix it a bit more + hashCode -= hashCode >> 17; + hashCode -= hashCode >> 11; + hashCode -= hashCode >> 5; + return hashCode; + } + } +} diff --git a/src/Shared/Compat/ValidateNames.cs b/src/Shared/Compat/ValidateNames.cs new file mode 100644 index 00000000000..4308b6049b4 --- /dev/null +++ b/src/Shared/Compat/ValidateNames.cs @@ -0,0 +1,682 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +using System; +#if !SILVERLIGHT && FEATURE_XPATH +using System.Xml.XPath; +#endif +using System.Diagnostics; +using System.Globalization; + +#if SILVERLIGHT_XPATH +namespace System.Xml.XPath { +#else +namespace System.Xml { +#endif + + /// + /// Contains various static functions and methods for parsing and validating: + /// NCName (not namespace-aware, no colons allowed) + /// QName (prefix:local-name) + /// + internal static class ValidateNames { + + internal enum Flags { + NCNames = 0x1, // Validate that each non-empty prefix and localName is a valid NCName + CheckLocalName = 0x2, // Validate the local-name + CheckPrefixMapping = 0x4, // Validate the prefix --> namespace mapping + All = 0x7, + AllExceptNCNames = 0x6, + AllExceptPrefixMapping = 0x3, + }; + + static XmlCharType xmlCharType = XmlCharType.Instance; + +#if !SILVERLIGHT + //----------------------------------------------- + // Nmtoken parsing + //----------------------------------------------- + /// + /// Attempts to parse the input string as an Nmtoken (see the XML spec production [7] && XML Namespaces spec). + /// Quits parsing when an invalid Nmtoken char is reached or the end of string is reached. + /// Returns the number of valid Nmtoken chars that were parsed. + /// + internal static unsafe int ParseNmtoken(string s, int offset) { + Debug.Assert(s != null && offset <= s.Length); + + // Keep parsing until the end of string or an invalid NCName character is reached + int i = offset; + while (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCNameSC) != 0) { // if (xmlCharType.IsNCNameSingleChar(s[i])) { + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + break; + } + } + + return i - offset; + } +#endif + + //----------------------------------------------- + // Nmtoken parsing (no XML namespaces support) + //----------------------------------------------- + /// + /// Attempts to parse the input string as an Nmtoken (see the XML spec production [7]) without taking + /// into account the XML Namespaces spec. What it means is that the ':' character is allowed at any + /// position and any number of times in the token. + /// Quits parsing when an invalid Nmtoken char is reached or the end of string is reached. + /// Returns the number of valid Nmtoken chars that were parsed. + /// +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal static unsafe int ParseNmtokenNoNamespaces(string s, int offset) { + + Debug.Assert(s != null && offset <= s.Length); + + // Keep parsing until the end of string or an invalid Name character is reached + int i = offset; + while (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCNameSC) != 0 || s[i] == ':') { // if (xmlCharType.IsNameSingleChar(s[i])) { + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + break; + } + } + + return i - offset; + } + + // helper methods + internal static bool IsNmtokenNoNamespaces(string s) { + int endPos = ParseNmtokenNoNamespaces(s, 0); + return endPos > 0 && endPos == s.Length; + } + + //----------------------------------------------- + // Name parsing (no XML namespaces support) + //----------------------------------------------- + /// + /// Attempts to parse the input string as a Name without taking into account the XML Namespaces spec. + /// What it means is that the ':' character does not delimiter prefix and local name, but it is a regular + /// name character, which is allowed to appear at any position and any number of times in the name. + /// Quits parsing when an invalid Name char is reached or the end of string is reached. + /// Returns the number of valid Name chars that were parsed. + /// +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal static unsafe int ParseNameNoNamespaces(string s, int offset) { + + Debug.Assert(s != null && offset <= s.Length); + + // Quit if the first character is not a valid NCName starting character + int i = offset; + if (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCStartNameSC) != 0 || s[i] == ':') { // xmlCharType.IsStartNCNameSingleChar(s[i])) { + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + return 0; // no valid StartNCName char + } + + // Keep parsing until the end of string or an invalid NCName character is reached + while (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCNameSC) != 0 || s[i] == ':') { // if (xmlCharType.IsNCNameSingleChar(s[i])) + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + break; + } + } + } + + return i - offset; + } + + // helper methods + internal static bool IsNameNoNamespaces(string s) { + int endPos = ParseNameNoNamespaces(s, 0); + return endPos > 0 && endPos == s.Length; + } + + //----------------------------------------------- + // NCName parsing + //----------------------------------------------- + + /// + /// Attempts to parse the input string as an NCName (see the XML Namespace spec). + /// Quits parsing when an invalid NCName char is reached or the end of string is reached. + /// Returns the number of valid NCName chars that were parsed. + /// +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal static unsafe int ParseNCName(string s, int offset) { + + Debug.Assert(s != null && offset <= s.Length); + + // Quit if the first character is not a valid NCName starting character + int i = offset; + if (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCStartNameSC) != 0) { // xmlCharType.IsStartNCNameSingleChar(s[i])) { + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + return 0; // no valid StartNCName char + } + + // Keep parsing until the end of string or an invalid NCName character is reached + while (i < s.Length) { + if ((xmlCharType.charProperties[s[i]] & XmlCharType.fNCNameSC) != 0) { // if (xmlCharType.IsNCNameSingleChar(s[i])) + i++; + } +#if XML10_FIFTH_EDITION + else if (xmlCharType.IsNCNameSurrogateChar(s, i)) { + i += 2; + } +#endif + else { + break; + } + } + } + + return i - offset; + } + + internal static int ParseNCName(string s) { + return ParseNCName(s, 0); + } + + /// + /// Calls parseName and throws exception if the resulting name is not a valid NCName. + /// Returns the input string if there is no error. + /// + internal static string ParseNCNameThrow(string s) { + // throwOnError = true + ParseNCNameInternal(s, true); + return s; + } + + /// + /// Calls parseName and returns false or throws exception if the resulting name is not + /// a valid NCName. Returns the input string if there is no error. + /// + private static bool ParseNCNameInternal(string s, bool throwOnError) { + int len = ParseNCName(s, 0); + + if (len == 0 || len != s.Length) { + // If the string is not a valid NCName, then throw or return false + if (throwOnError) ThrowInvalidName(s, 0, len); + return false; + } + + return true; + } + + //----------------------------------------------- + // QName parsing + //----------------------------------------------- + + /// + /// Attempts to parse the input string as a QName (see the XML Namespace spec). + /// Quits parsing when an invalid QName char is reached or the end of string is reached. + /// Returns the number of valid QName chars that were parsed. + /// Sets colonOffset to the offset of a colon character if it exists, or 0 otherwise. + /// + internal static int ParseQName(string s, int offset, out int colonOffset) { + int len, lenLocal; + + // Assume no colon + colonOffset = 0; + + // Parse NCName (may be prefix, may be local name) + len = ParseNCName(s, offset); + if (len != 0) { + + // Non-empty NCName, so look for colon if there are any characters left + offset += len; + if (offset < s.Length && s[offset] == ':') { + + // First NCName was prefix, so look for local name part + lenLocal = ParseNCName(s, offset + 1); + if (lenLocal != 0) { + // Local name part found, so increase total QName length (add 1 for colon) + colonOffset = offset; + len += lenLocal + 1; + } + } + } + + return len; + } + + /// + /// Calls parseQName and throws exception if the resulting name is not a valid QName. + /// Returns the prefix and local name parts. + /// + internal static void ParseQNameThrow(string s, out string prefix, out string localName) { + int colonOffset; + int len = ParseQName(s, 0, out colonOffset); + + if (len == 0 || len != s.Length) { + // If the string is not a valid QName, then throw + ThrowInvalidName(s, 0, len); + } + + if (colonOffset != 0) { + prefix = s.Substring(0, colonOffset); + localName = s.Substring(colonOffset + 1); + } + else { + prefix = ""; + localName = s; + } + } + +#if !SILVERLIGHT + /// + /// Parses the input string as a NameTest (see the XPath spec), returning the prefix and + /// local name parts. Throws an exception if the given string is not a valid NameTest. + /// If the NameTest contains a star, null values for localName (case NCName':*'), or for + /// both localName and prefix (case '*') are returned. + /// + internal static void ParseNameTestThrow(string s, out string prefix, out string localName) { + int len, lenLocal, offset; + + if (s.Length != 0 && s[0] == '*') { + // '*' as a NameTest + prefix = localName = null; + len = 1; + } + else { + // Parse NCName (may be prefix, may be local name) + len = ParseNCName(s, 0); + if (len != 0) { + + // Non-empty NCName, so look for colon if there are any characters left + localName = s.Substring(0, len); + if (len < s.Length && s[len] == ':') { + + // First NCName was prefix, so look for local name part + prefix = localName; + offset = len + 1; + if (offset < s.Length && s[offset] == '*') { + // '*' as a local name part, add 2 to len for colon and star + localName = null; + len += 2; + } + else { + lenLocal = ParseNCName(s, offset); + if (lenLocal != 0) { + // Local name part found, so increase total NameTest length + localName = s.Substring(offset, lenLocal); + len += lenLocal + 1; + } + } + } + else { + prefix = string.Empty; + } + } + else { + // Make the compiler happy + prefix = localName = null; + } + } + + if (len == 0 || len != s.Length) { + // If the string is not a valid NameTest, then throw + ThrowInvalidName(s, 0, len); + } + } +#endif + + /// + /// Throws an invalid name exception. + /// + /// String that was parsed. + /// Offset in string where parsing began. + /// Offset in string where parsing failed. + internal static void ThrowInvalidName(string s, int offsetStartChar, int offsetBadChar) { + // If the name is empty, throw an exception + if (offsetStartChar >= s.Length) +#if !SILVERLIGHT_XPATH + throw new XmlException("Xml_EmptyName"); +#else + throw new XmlException(Res.GetString(Res.Xml_EmptyName, string.Empty)); +#endif + + Debug.Assert(offsetBadChar < s.Length); + + if (xmlCharType.IsNCNameSingleChar(s[offsetBadChar]) && !XmlCharType.Instance.IsStartNCNameSingleChar(s[offsetBadChar])) { + // The error character is a valid name character, but is not a valid start name character +#if !SILVERLIGHT_XPATH + //throw new XmlException(Res.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs(s, offsetBadChar)); + throw new XmlException("Xml_BadStartNameChar"); +#else + throw new XmlException(Res.GetString(Res.Xml_BadStartNameChar, XmlExceptionHelper.BuildCharExceptionArgs(s, offsetBadChar))); +#endif + } + else { + // The error character is an invalid name character +#if !SILVERLIGHT_XPATH + //throw new XmlException(Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(s, offsetBadChar)); + throw new XmlException("Xml_BadNameChar"); +#else + throw new XmlException(Res.GetString(Res.Xml_BadNameChar, XmlExceptionHelper.BuildCharExceptionArgs(s, offsetBadChar))); +#endif + } + } + +#if !SILVERLIGHT + internal static Exception GetInvalidNameException(string s, int offsetStartChar, int offsetBadChar) { + // If the name is empty, throw an exception + if (offsetStartChar >= s.Length) + return new XmlException("Res.Xml_EmptyName"); + + Debug.Assert(offsetBadChar < s.Length); + + if (xmlCharType.IsNCNameSingleChar(s[offsetBadChar]) && !xmlCharType.IsStartNCNameSingleChar(s[offsetBadChar])) { + // The error character is a valid name character, but is not a valid start name character + //return new XmlException(Res.Xml_BadStartNameChar, XmlException.BuildCharExceptionArgs(s, offsetBadChar)); + return new XmlException("Xml_BadStartNameChar"); + } + else { + // The error character is an invalid name character + //return new XmlException(Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(s, offsetBadChar)); + return new XmlException("Xml_BadNameChar"); + } + } + + /// + /// Returns true if "prefix" starts with the characters 'x', 'm', 'l' (case-insensitive). + /// + internal static bool StartsWithXml(string s) { + if (s.Length < 3) + return false; + + if (s[0] != 'x' && s[0] != 'X') + return false; + + if (s[1] != 'm' && s[1] != 'M') + return false; + + if (s[2] != 'l' && s[2] != 'L') + return false; + + return true; + } + + /// + /// Returns true if "s" is a namespace that is reserved by Xml 1.0 or Namespace 1.0. + /// + internal static bool IsReservedNamespace(string s) { + return s.Equals(XmlReservedNs.NsXml) || s.Equals(XmlReservedNs.NsXmlNs); + } + +#if FEATURE_XPATH + /// + /// Throw if the specified name parts are not valid according to the rules of "nodeKind". Check only rules that are + /// specified by the Flags. + /// NOTE: Namespaces should be passed using a prefix, ns pair. "localName" is always string.Empty. + /// + internal static void ValidateNameThrow(string prefix, string localName, string ns, XPathNodeType nodeKind, Flags flags) { + // throwOnError = true + ValidateNameInternal(prefix, localName, ns, nodeKind, flags, true); + } + + /// + /// Return false if the specified name parts are not valid according to the rules of "nodeKind". Check only rules that are + /// specified by the Flags. + /// NOTE: Namespaces should be passed using a prefix, ns pair. "localName" is always string.Empty. + /// + internal static bool ValidateName(string prefix, string localName, string ns, XPathNodeType nodeKind, Flags flags) { + // throwOnError = false + return ValidateNameInternal(prefix, localName, ns, nodeKind, flags, false); + } + + /// + /// Return false or throw if the specified name parts are not valid according to the rules of "nodeKind". Check only rules + /// that are specified by the Flags. + /// NOTE: Namespaces should be passed using a prefix, ns pair. "localName" is always string.Empty. + /// + private static bool ValidateNameInternal(string prefix, string localName, string ns, XPathNodeType nodeKind, Flags flags, bool throwOnError) { + Debug.Assert(prefix != null && localName != null && ns != null); + + if ((flags & Flags.NCNames) != 0) { + + // 1. Verify that each non-empty prefix and localName is a valid NCName + if (prefix.Length != 0) + if (!ParseNCNameInternal(prefix, throwOnError)) { + return false; + } + + if (localName.Length != 0) + if (!ParseNCNameInternal(localName, throwOnError)) { + return false; + } + } + + if ((flags & Flags.CheckLocalName) != 0) { + + // 2. Determine whether the local name is valid + switch (nodeKind) { + case XPathNodeType.Element: + // Elements and attributes must have a non-empty local name + if (localName.Length == 0) { + if (throwOnError) throw new XmlException(Res.Xdom_Empty_LocalName, string.Empty); + return false; + } + break; + + case XPathNodeType.Attribute: + // Attribute local name cannot be "xmlns" if namespace is empty + if (ns.Length == 0 && localName.Equals("xmlns")) { + if (throwOnError) throw new XmlException(Res.XmlBadName, new string[] {nodeKind.ToString(), localName}); + return false; + } + goto case XPathNodeType.Element; + + case XPathNodeType.ProcessingInstruction: + // PI's local-name must be non-empty and cannot be 'xml' (case-insensitive) + if (localName.Length == 0 || (localName.Length == 3 && StartsWithXml(localName))) { + if (throwOnError) throw new XmlException(Res.Xml_InvalidPIName, localName); + return false; + } + break; + + default: + // All other node types must have empty local-name + if (localName.Length != 0) { + if (throwOnError) throw new XmlException(Res.XmlNoNameAllowed, nodeKind.ToString()); + return false; + } + break; + } + } + + if ((flags & Flags.CheckPrefixMapping) != 0) { + + // 3. Determine whether the prefix is valid + switch (nodeKind) { + case XPathNodeType.Element: + case XPathNodeType.Attribute: + case XPathNodeType.Namespace: + if (ns.Length == 0) { + // If namespace is empty, then prefix must be empty + if (prefix.Length != 0) { + if (throwOnError) throw new XmlException(Res.Xml_PrefixForEmptyNs, string.Empty); + return false; + } + } + else { + // Don't allow empty attribute prefix since namespace is non-empty + if (prefix.Length == 0 && nodeKind == XPathNodeType.Attribute) { + if (throwOnError) throw new XmlException(Res.XmlBadName, new string[] {nodeKind.ToString(), localName}); + return false; + } + + if (prefix.Equals("xml")) { + // xml prefix must be mapped to the xml namespace + if (!ns.Equals(XmlReservedNs.NsXml)) { + if (throwOnError) throw new XmlException(Res.Xml_XmlPrefix, string.Empty); + return false; + } + } + else if (prefix.Equals("xmlns")) { + // Prefix may never be 'xmlns' + if (throwOnError) throw new XmlException(Res.Xml_XmlnsPrefix, string.Empty); + return false; + } + else if (IsReservedNamespace(ns)) { + // Don't allow non-reserved prefixes to map to xml or xmlns namespaces + if (throwOnError) throw new XmlException(Res.Xml_NamespaceDeclXmlXmlns, string.Empty); + return false; + } + } + break; + + case XPathNodeType.ProcessingInstruction: + // PI's prefix and namespace must be empty + if (prefix.Length != 0 || ns.Length != 0) { + if (throwOnError) throw new XmlException(Res.Xml_InvalidPIName, CreateName(prefix, localName)); + return false; + } + break; + + default: + // All other node types must have empty prefix and namespace + if (prefix.Length != 0 || ns.Length != 0) { + if (throwOnError) throw new XmlException(Res.XmlNoNameAllowed, nodeKind.ToString()); + return false; + } + break; + } + } + + return true; + } +#endif + + /// + /// Creates a colon-delimited qname from prefix and local name parts. + /// + private static string CreateName(string prefix, string localName) { + return (prefix.Length != 0) ? prefix + ":" + localName : localName; + } +#endif + + +#if !SILVERLIGHT || SILVERLIGHT_XPATH + /// + /// Split a QualifiedName into prefix and localname, w/o any checking. + /// (Used for XmlReader/XPathNavigator MoveTo(name) methods) + /// + internal static void SplitQName(string name, out string prefix, out string lname) { + int colonPos = name.IndexOf(':'); + if (-1 == colonPos) { + prefix = string.Empty; + lname = name; + } + else if (0 == colonPos || (name.Length-1) == colonPos) { +#if !SILVERLIGHT_XPATH + //throw new ArgumentException(Res.GetString(Res.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(':', '\0')), "name"); + throw new ArgumentException("Xml_BadNameChar"); +#else + throw new ArgumentException(Res.GetString(Res.Xml_BadNameChar, XmlExceptionHelper.BuildCharExceptionArgs(':', '\0')), "name"); +#endif + } + else { + prefix = name.Substring(0, colonPos); + colonPos++; // move after colon + lname = name.Substring(colonPos, name.Length - colonPos); + } + } +#endif + } + +#if SILVERLIGHT_XPATH + internal class XmlExceptionHelper + { + internal static string[] BuildCharExceptionArgs(string data, int invCharIndex) + { + return BuildCharExceptionArgs(data[invCharIndex], invCharIndex + 1 < data.Length ? data[invCharIndex + 1] : '\0'); + } + + internal static string[] BuildCharExceptionArgs(char[] data, int invCharIndex) + { + return BuildCharExceptionArgs(data, data.Length, invCharIndex); + } + + internal static string[] BuildCharExceptionArgs(char[] data, int length, int invCharIndex) + { + Debug.Assert(invCharIndex < data.Length); + Debug.Assert(invCharIndex < length); + Debug.Assert(length <= data.Length); + + return BuildCharExceptionArgs(data[invCharIndex], invCharIndex + 1 < length ? data[invCharIndex + 1] : '\0'); + } + + internal static string[] BuildCharExceptionArgs(char invChar, char nextChar) + { + string[] aStringList = new string[2]; + + // for surrogate characters include both high and low char in the message so that a full character is displayed + if (XmlCharType.IsHighSurrogate(invChar) && nextChar != 0) + { + int combinedChar = XmlCharType.CombineSurrogateChar(nextChar, invChar); + aStringList[0] = new string(new char[] { invChar, nextChar }); + aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", combinedChar); + } + else + { + // don't include 0 character in the string - in means eof-of-string in native code, where this may bubble up to + if ((int)invChar == 0) + { + aStringList[0] = "."; + } + else + { + aStringList[0] = invChar.ToString(CultureInfo.InvariantCulture); + } + aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", (int)invChar); + } + return aStringList; + } + } +#endif +} diff --git a/src/Shared/Compat/XmlCharType.cs b/src/Shared/Compat/XmlCharType.cs new file mode 100644 index 00000000000..46237198c2f --- /dev/null +++ b/src/Shared/Compat/XmlCharType.cs @@ -0,0 +1,796 @@ +#if XMLCHARTYPE_GEN_RESOURCE +#undef XMLCHARTYPE_USE_RESOURCE +#endif + +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +//#define XMLCHARTYPE_USE_RESOURCE // load the character properties from resources (XmlCharType.bin must be linked to System.Xml.dll) +//#define XMLCHARTYPE_GEN_RESOURCE // generate the character properties into XmlCharType.bin + +#if XMLCHARTYPE_GEN_RESOURCE || XMLCHARTYPE_USE_RESOURCE +using System.IO; +using System.Reflection; +#endif +using System.Threading; +using System.Diagnostics; + +namespace System.Xml { + + /// + /// + /// + /// The XmlCharType class is used for quick character type recognition + /// which is optimized for the first 127 ascii characters. + /// +#if SILVERLIGHT +#if !SILVERLIGHT_XPATH + [System.Runtime.CompilerServices.FriendAccessAllowed] // Used by System.ServiceModel.dll and System.Runtime.Serialization.dll +#endif +#endif +#if XMLCHARTYPE_USE_RESOURCE + unsafe internal struct XmlCharType { +#else + internal struct XmlCharType { +#endif + // Surrogate constants + internal const int SurHighStart = 0xd800; // 1101 10xx + internal const int SurHighEnd = 0xdbff; + internal const int SurLowStart = 0xdc00; // 1101 11xx + internal const int SurLowEnd = 0xdfff; + internal const int SurMask = 0xfc00; // 1111 11xx + +#if XML10_FIFTH_EDITION + // Characters defined in the XML 1.0 Fifth Edition + // Whitespace chars -- Section 2.3 [3] + // Star NCName characters -- Section 2.3 [4] (NameStartChar characters without ':') + // NCName characters -- Section 2.3 [4a] (NameChar characters without ':') + // Character data characters -- Section 2.2 [2] + // Public ID characters -- Section 2.3 [13] + + // Characters defined in the XML 1.0 Fourth Edition + // NCNameCharacters -- Appending B: Characters Classes in XML 1.0 4th edition and earlier - minus the ':' char per the Namespaces in XML spec + // Letter characters -- Appending B: Characters Classes in XML 1.0 4th edition and earlier + // This appendix has been deprecated in XML 1.0 5th edition, but we still need to use + // the Letter and NCName definitions from the 4th edition in some places because of backwards compatibility + internal const int fWhitespace = 1; + internal const int fLetter = 2; + internal const int fNCStartNameSC = 4; // SC = Single Char + internal const int fNCNameSC = 8; // SC = Single Char + internal const int fCharData = 16; + internal const int fNCNameXml4e = 32; // NCName char according to the XML 1.0 4th edition + internal const int fText = 64; + internal const int fAttrValue = 128; + + // name surrogate constants + private const int s_NCNameSurCombinedStart = 0x10000; + private const int s_NCNameSurCombinedEnd = 0xEFFFF; + private const int s_NCNameSurHighStart = SurHighStart + ((s_NCNameSurCombinedStart - 0x10000) / 1024); + private const int s_NCNameSurHighEnd = SurHighStart + ((s_NCNameSurCombinedEnd - 0x10000) / 1024); + private const int s_NCNameSurLowStart = SurLowStart + ((s_NCNameSurCombinedStart - 0x10000) % 1024); + private const int s_NCNameSurLowEnd = SurLowStart + ((s_NCNameSurCombinedEnd - 0x10000) % 1024); +#else + // Characters defined in the XML 1.0 Fourth Edition + // Whitespace chars -- Section 2.3 [3] + // Letters -- Appendix B [84] + // Starting NCName characters -- Section 2.3 [5] (Starting Name characters without ':') + // NCName characters -- Section 2.3 [4] (Name characters without ':') + // Character data characters -- Section 2.2 [2] + // PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] Section 2.3 of spec + internal const int fWhitespace = 1; + internal const int fLetter = 2; + internal const int fNCStartNameSC = 4; + internal const int fNCNameSC = 8; + internal const int fCharData = 16; + internal const int fNCNameXml4e = 32; + internal const int fText = 64; + internal const int fAttrValue = 128; +#endif + + // bitmap for public ID characters - 1 bit per character 0x0 - 0x80; no character > 0x80 is a PUBLIC ID char + private const string s_PublicIdBitmap = "\u2400\u0000\uffbb\uafff\uffff\u87ff\ufffe\u07ff"; + + // size of XmlCharType table + private const uint CharPropertiesSize = (uint)char.MaxValue + 1; + +#if !XMLCHARTYPE_USE_RESOURCE || XMLCHARTYPE_GEN_RESOURCE + internal const string s_Whitespace = + "\u0009\u000a\u000d\u000d\u0020\u0020"; + +#if XML10_FIFTH_EDITION + // StartNameChar without ':' -- see Section 2.3 production [4] + const string s_NCStartName = + "\u0041\u005a\u005f\u005f\u0061\u007a\u00c0\u00d6" + + "\u00d8\u00f6\u00f8\u02ff\u0370\u037d\u037f\u1fff" + + "\u200c\u200d\u2070\u218f\u2c00\u2fef\u3001\ud7ff" + + "\uf900\ufdcf\ufdf0\ufffd"; + + // NameChar without ':' -- see Section 2.3 production [4a] + const string s_NCName = + "\u002d\u002e\u0030\u0039\u0041\u005a\u005f\u005f" + + "\u0061\u007a\u00b7\u00b7\u00c0\u00d6\u00d8\u00f6" + + "\u00f8\u037d\u037f\u1fff\u200c\u200d\u203f\u2040" + + "\u2070\u218f\u2c00\u2fef\u3001\ud7ff\uf900\ufdcf" + + "\ufdf0\ufffd"; +#else + const string s_NCStartName = + "\u0041\u005a\u005f\u005f\u0061\u007a" + + "\u00c0\u00d6\u00d8\u00f6\u00f8\u0131\u0134\u013e" + + "\u0141\u0148\u014a\u017e\u0180\u01c3\u01cd\u01f0" + + "\u01f4\u01f5\u01fa\u0217\u0250\u02a8\u02bb\u02c1" + + "\u0386\u0386\u0388\u038a\u038c\u038c\u038e\u03a1" + + "\u03a3\u03ce\u03d0\u03d6\u03da\u03da\u03dc\u03dc" + + "\u03de\u03de\u03e0\u03e0\u03e2\u03f3\u0401\u040c" + + "\u040e\u044f\u0451\u045c\u045e\u0481\u0490\u04c4" + + "\u04c7\u04c8\u04cb\u04cc\u04d0\u04eb\u04ee\u04f5" + + "\u04f8\u04f9\u0531\u0556\u0559\u0559\u0561\u0586" + + "\u05d0\u05ea\u05f0\u05f2\u0621\u063a\u0641\u064a" + + "\u0671\u06b7\u06ba\u06be\u06c0\u06ce\u06d0\u06d3" + + "\u06d5\u06d5\u06e5\u06e6\u0905\u0939\u093d\u093d" + + "\u0958\u0961\u0985\u098c\u098f\u0990\u0993\u09a8" + + "\u09aa\u09b0\u09b2\u09b2\u09b6\u09b9\u09dc\u09dd" + + "\u09df\u09e1\u09f0\u09f1\u0a05\u0a0a\u0a0f\u0a10" + + "\u0a13\u0a28\u0a2a\u0a30\u0a32\u0a33\u0a35\u0a36" + + "\u0a38\u0a39\u0a59\u0a5c\u0a5e\u0a5e\u0a72\u0a74" + + "\u0a85\u0a8b\u0a8d\u0a8d\u0a8f\u0a91\u0a93\u0aa8" + + "\u0aaa\u0ab0\u0ab2\u0ab3\u0ab5\u0ab9\u0abd\u0abd" + + "\u0ae0\u0ae0\u0b05\u0b0c\u0b0f\u0b10\u0b13\u0b28" + + "\u0b2a\u0b30\u0b32\u0b33\u0b36\u0b39\u0b3d\u0b3d" + + "\u0b5c\u0b5d\u0b5f\u0b61\u0b85\u0b8a\u0b8e\u0b90" + + "\u0b92\u0b95\u0b99\u0b9a\u0b9c\u0b9c\u0b9e\u0b9f" + + "\u0ba3\u0ba4\u0ba8\u0baa\u0bae\u0bb5\u0bb7\u0bb9" + + "\u0c05\u0c0c\u0c0e\u0c10\u0c12\u0c28\u0c2a\u0c33" + + "\u0c35\u0c39\u0c60\u0c61\u0c85\u0c8c\u0c8e\u0c90" + + "\u0c92\u0ca8\u0caa\u0cb3\u0cb5\u0cb9\u0cde\u0cde" + + "\u0ce0\u0ce1\u0d05\u0d0c\u0d0e\u0d10\u0d12\u0d28" + + "\u0d2a\u0d39\u0d60\u0d61\u0e01\u0e2e\u0e30\u0e30" + + "\u0e32\u0e33\u0e40\u0e45\u0e81\u0e82\u0e84\u0e84" + + "\u0e87\u0e88\u0e8a\u0e8a\u0e8d\u0e8d\u0e94\u0e97" + + "\u0e99\u0e9f\u0ea1\u0ea3\u0ea5\u0ea5\u0ea7\u0ea7" + + "\u0eaa\u0eab\u0ead\u0eae\u0eb0\u0eb0\u0eb2\u0eb3" + + "\u0ebd\u0ebd\u0ec0\u0ec4\u0f40\u0f47\u0f49\u0f69" + + "\u10a0\u10c5\u10d0\u10f6\u1100\u1100\u1102\u1103" + + "\u1105\u1107\u1109\u1109\u110b\u110c\u110e\u1112" + + "\u113c\u113c\u113e\u113e\u1140\u1140\u114c\u114c" + + "\u114e\u114e\u1150\u1150\u1154\u1155\u1159\u1159" + + "\u115f\u1161\u1163\u1163\u1165\u1165\u1167\u1167" + + "\u1169\u1169\u116d\u116e\u1172\u1173\u1175\u1175" + + "\u119e\u119e\u11a8\u11a8\u11ab\u11ab\u11ae\u11af" + + "\u11b7\u11b8\u11ba\u11ba\u11bc\u11c2\u11eb\u11eb" + + "\u11f0\u11f0\u11f9\u11f9\u1e00\u1e9b\u1ea0\u1ef9" + + "\u1f00\u1f15\u1f18\u1f1d\u1f20\u1f45\u1f48\u1f4d" + + "\u1f50\u1f57\u1f59\u1f59\u1f5b\u1f5b\u1f5d\u1f5d" + + "\u1f5f\u1f7d\u1f80\u1fb4\u1fb6\u1fbc\u1fbe\u1fbe" + + "\u1fc2\u1fc4\u1fc6\u1fcc\u1fd0\u1fd3\u1fd6\u1fdb" + + "\u1fe0\u1fec\u1ff2\u1ff4\u1ff6\u1ffc\u2126\u2126" + + "\u212a\u212b\u212e\u212e\u2180\u2182\u3007\u3007" + + "\u3021\u3029\u3041\u3094\u30a1\u30fa\u3105\u312c" + + "\u4e00\u9fa5\uac00\ud7a3"; + + const string s_NCName = + "\u002d\u002e\u0030\u0039\u0041\u005a\u005f\u005f" + + "\u0061\u007a\u00b7\u00b7\u00c0\u00d6\u00d8\u00f6" + + "\u00f8\u0131\u0134\u013e\u0141\u0148\u014a\u017e" + + "\u0180\u01c3\u01cd\u01f0\u01f4\u01f5\u01fa\u0217" + + "\u0250\u02a8\u02bb\u02c1\u02d0\u02d1\u0300\u0345" + + "\u0360\u0361\u0386\u038a\u038c\u038c\u038e\u03a1" + + "\u03a3\u03ce\u03d0\u03d6\u03da\u03da\u03dc\u03dc" + + "\u03de\u03de\u03e0\u03e0\u03e2\u03f3\u0401\u040c" + + "\u040e\u044f\u0451\u045c\u045e\u0481\u0483\u0486" + + "\u0490\u04c4\u04c7\u04c8\u04cb\u04cc\u04d0\u04eb" + + "\u04ee\u04f5\u04f8\u04f9\u0531\u0556\u0559\u0559" + + "\u0561\u0586\u0591\u05a1\u05a3\u05b9\u05bb\u05bd" + + "\u05bf\u05bf\u05c1\u05c2\u05c4\u05c4\u05d0\u05ea" + + "\u05f0\u05f2\u0621\u063a\u0640\u0652\u0660\u0669" + + "\u0670\u06b7\u06ba\u06be\u06c0\u06ce\u06d0\u06d3" + + "\u06d5\u06e8\u06ea\u06ed\u06f0\u06f9\u0901\u0903" + + "\u0905\u0939\u093c\u094d\u0951\u0954\u0958\u0963" + + "\u0966\u096f\u0981\u0983\u0985\u098c\u098f\u0990" + + "\u0993\u09a8\u09aa\u09b0\u09b2\u09b2\u09b6\u09b9" + + "\u09bc\u09bc\u09be\u09c4\u09c7\u09c8\u09cb\u09cd" + + "\u09d7\u09d7\u09dc\u09dd\u09df\u09e3\u09e6\u09f1" + + "\u0a02\u0a02\u0a05\u0a0a\u0a0f\u0a10\u0a13\u0a28" + + "\u0a2a\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39" + + "\u0a3c\u0a3c\u0a3e\u0a42\u0a47\u0a48\u0a4b\u0a4d" + + "\u0a59\u0a5c\u0a5e\u0a5e\u0a66\u0a74\u0a81\u0a83" + + "\u0a85\u0a8b\u0a8d\u0a8d\u0a8f\u0a91\u0a93\u0aa8" + + "\u0aaa\u0ab0\u0ab2\u0ab3\u0ab5\u0ab9\u0abc\u0ac5" + + "\u0ac7\u0ac9\u0acb\u0acd\u0ae0\u0ae0\u0ae6\u0aef" + + "\u0b01\u0b03\u0b05\u0b0c\u0b0f\u0b10\u0b13\u0b28" + + "\u0b2a\u0b30\u0b32\u0b33\u0b36\u0b39\u0b3c\u0b43" + + "\u0b47\u0b48\u0b4b\u0b4d\u0b56\u0b57\u0b5c\u0b5d" + + "\u0b5f\u0b61\u0b66\u0b6f\u0b82\u0b83\u0b85\u0b8a" + + "\u0b8e\u0b90\u0b92\u0b95\u0b99\u0b9a\u0b9c\u0b9c" + + "\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0baa\u0bae\u0bb5" + + "\u0bb7\u0bb9\u0bbe\u0bc2\u0bc6\u0bc8\u0bca\u0bcd" + + "\u0bd7\u0bd7\u0be7\u0bef\u0c01\u0c03\u0c05\u0c0c" + + "\u0c0e\u0c10\u0c12\u0c28\u0c2a\u0c33\u0c35\u0c39" + + "\u0c3e\u0c44\u0c46\u0c48\u0c4a\u0c4d\u0c55\u0c56" + + "\u0c60\u0c61\u0c66\u0c6f\u0c82\u0c83\u0c85\u0c8c" + + "\u0c8e\u0c90\u0c92\u0ca8\u0caa\u0cb3\u0cb5\u0cb9" + + "\u0cbe\u0cc4\u0cc6\u0cc8\u0cca\u0ccd\u0cd5\u0cd6" + + "\u0cde\u0cde\u0ce0\u0ce1\u0ce6\u0cef\u0d02\u0d03" + + "\u0d05\u0d0c\u0d0e\u0d10\u0d12\u0d28\u0d2a\u0d39" + + "\u0d3e\u0d43\u0d46\u0d48\u0d4a\u0d4d\u0d57\u0d57" + + "\u0d60\u0d61\u0d66\u0d6f\u0e01\u0e2e\u0e30\u0e3a" + + "\u0e40\u0e4e\u0e50\u0e59\u0e81\u0e82\u0e84\u0e84" + + "\u0e87\u0e88\u0e8a\u0e8a\u0e8d\u0e8d\u0e94\u0e97" + + "\u0e99\u0e9f\u0ea1\u0ea3\u0ea5\u0ea5\u0ea7\u0ea7" + + "\u0eaa\u0eab\u0ead\u0eae\u0eb0\u0eb9\u0ebb\u0ebd" + + "\u0ec0\u0ec4\u0ec6\u0ec6\u0ec8\u0ecd\u0ed0\u0ed9" + + "\u0f18\u0f19\u0f20\u0f29\u0f35\u0f35\u0f37\u0f37" + + "\u0f39\u0f39\u0f3e\u0f47\u0f49\u0f69\u0f71\u0f84" + + "\u0f86\u0f8b\u0f90\u0f95\u0f97\u0f97\u0f99\u0fad" + + "\u0fb1\u0fb7\u0fb9\u0fb9\u10a0\u10c5\u10d0\u10f6" + + "\u1100\u1100\u1102\u1103\u1105\u1107\u1109\u1109" + + "\u110b\u110c\u110e\u1112\u113c\u113c\u113e\u113e" + + "\u1140\u1140\u114c\u114c\u114e\u114e\u1150\u1150" + + "\u1154\u1155\u1159\u1159\u115f\u1161\u1163\u1163" + + "\u1165\u1165\u1167\u1167\u1169\u1169\u116d\u116e" + + "\u1172\u1173\u1175\u1175\u119e\u119e\u11a8\u11a8" + + "\u11ab\u11ab\u11ae\u11af\u11b7\u11b8\u11ba\u11ba" + + "\u11bc\u11c2\u11eb\u11eb\u11f0\u11f0\u11f9\u11f9" + + "\u1e00\u1e9b\u1ea0\u1ef9\u1f00\u1f15\u1f18\u1f1d" + + "\u1f20\u1f45\u1f48\u1f4d\u1f50\u1f57\u1f59\u1f59" + + "\u1f5b\u1f5b\u1f5d\u1f5d\u1f5f\u1f7d\u1f80\u1fb4" + + "\u1fb6\u1fbc\u1fbe\u1fbe\u1fc2\u1fc4\u1fc6\u1fcc" + + "\u1fd0\u1fd3\u1fd6\u1fdb\u1fe0\u1fec\u1ff2\u1ff4" + + "\u1ff6\u1ffc\u20d0\u20dc\u20e1\u20e1\u2126\u2126" + + "\u212a\u212b\u212e\u212e\u2180\u2182\u3005\u3005" + + "\u3007\u3007\u3021\u302f\u3031\u3035\u3041\u3094" + + "\u3099\u309a\u309d\u309e\u30a1\u30fa\u30fc\u30fe" + + "\u3105\u312c\u4e00\u9fa5\uac00\ud7a3"; +#endif + + const string s_CharData = + "\u0009\u000a\u000d\u000d\u0020\ud7ff\ue000\ufffd"; + + const string s_PublicID = + "\u000a\u000a\u000d\u000d\u0020\u0021\u0023\u0025" + + "\u0027\u003b\u003d\u003d\u003f\u005a\u005f\u005f" + + "\u0061\u007a"; + + const string s_Text = // TextChar = CharData - { 0xA | 0xD | '<' | '&' | 0x9 | ']' | 0xDC00 - 0xDFFF } + "\u0020\u0025\u0027\u003b\u003d\u005c\u005e\ud7ff\ue000\ufffd"; + + const string s_AttrValue = // AttrValueChar = CharData - { 0xA | 0xD | 0x9 | '<' | '>' | '&' | '\'' | '"' | 0xDC00 - 0xDFFF } + "\u0020\u0021\u0023\u0025\u0028\u003b\u003d\u003d\u003f\ud7ff\ue000\ufffd"; + + // + // XML 1.0 Fourth Edition definitions for name characters + // + const string s_LetterXml4e = + "\u0041\u005a\u0061\u007a\u00c0\u00d6\u00d8\u00f6" + + "\u00f8\u0131\u0134\u013e\u0141\u0148\u014a\u017e" + + "\u0180\u01c3\u01cd\u01f0\u01f4\u01f5\u01fa\u0217" + + "\u0250\u02a8\u02bb\u02c1\u0386\u0386\u0388\u038a" + + "\u038c\u038c\u038e\u03a1\u03a3\u03ce\u03d0\u03d6" + + "\u03da\u03da\u03dc\u03dc\u03de\u03de\u03e0\u03e0" + + "\u03e2\u03f3\u0401\u040c\u040e\u044f\u0451\u045c" + + "\u045e\u0481\u0490\u04c4\u04c7\u04c8\u04cb\u04cc" + + "\u04d0\u04eb\u04ee\u04f5\u04f8\u04f9\u0531\u0556" + + "\u0559\u0559\u0561\u0586\u05d0\u05ea\u05f0\u05f2" + + "\u0621\u063a\u0641\u064a\u0671\u06b7\u06ba\u06be" + + "\u06c0\u06ce\u06d0\u06d3\u06d5\u06d5\u06e5\u06e6" + + "\u0905\u0939\u093d\u093d\u0958\u0961\u0985\u098c" + + "\u098f\u0990\u0993\u09a8\u09aa\u09b0\u09b2\u09b2" + + "\u09b6\u09b9\u09dc\u09dd\u09df\u09e1\u09f0\u09f1" + + "\u0a05\u0a0a\u0a0f\u0a10\u0a13\u0a28\u0a2a\u0a30" + + "\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5c" + + "\u0a5e\u0a5e\u0a72\u0a74\u0a85\u0a8b\u0a8d\u0a8d" + + "\u0a8f\u0a91\u0a93\u0aa8\u0aaa\u0ab0\u0ab2\u0ab3" + + "\u0ab5\u0ab9\u0abd\u0abd\u0ae0\u0ae0\u0b05\u0b0c" + + "\u0b0f\u0b10\u0b13\u0b28\u0b2a\u0b30\u0b32\u0b33" + + "\u0b36\u0b39\u0b3d\u0b3d\u0b5c\u0b5d\u0b5f\u0b61" + + "\u0b85\u0b8a\u0b8e\u0b90\u0b92\u0b95\u0b99\u0b9a" + + "\u0b9c\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0baa" + + "\u0bae\u0bb5\u0bb7\u0bb9\u0c05\u0c0c\u0c0e\u0c10" + + "\u0c12\u0c28\u0c2a\u0c33\u0c35\u0c39\u0c60\u0c61" + + "\u0c85\u0c8c\u0c8e\u0c90\u0c92\u0ca8\u0caa\u0cb3" + + "\u0cb5\u0cb9\u0cde\u0cde\u0ce0\u0ce1\u0d05\u0d0c" + + "\u0d0e\u0d10\u0d12\u0d28\u0d2a\u0d39\u0d60\u0d61" + + "\u0e01\u0e2e\u0e30\u0e30\u0e32\u0e33\u0e40\u0e45" + + "\u0e81\u0e82\u0e84\u0e84\u0e87\u0e88\u0e8a\u0e8a" + + "\u0e8d\u0e8d\u0e94\u0e97\u0e99\u0e9f\u0ea1\u0ea3" + + "\u0ea5\u0ea5\u0ea7\u0ea7\u0eaa\u0eab\u0ead\u0eae" + + "\u0eb0\u0eb0\u0eb2\u0eb3\u0ebd\u0ebd\u0ec0\u0ec4" + + "\u0f40\u0f47\u0f49\u0f69\u10a0\u10c5\u10d0\u10f6" + + "\u1100\u1100\u1102\u1103\u1105\u1107\u1109\u1109" + + "\u110b\u110c\u110e\u1112\u113c\u113c\u113e\u113e" + + "\u1140\u1140\u114c\u114c\u114e\u114e\u1150\u1150" + + "\u1154\u1155\u1159\u1159\u115f\u1161\u1163\u1163" + + "\u1165\u1165\u1167\u1167\u1169\u1169\u116d\u116e" + + "\u1172\u1173\u1175\u1175\u119e\u119e\u11a8\u11a8" + + "\u11ab\u11ab\u11ae\u11af\u11b7\u11b8\u11ba\u11ba" + + "\u11bc\u11c2\u11eb\u11eb\u11f0\u11f0\u11f9\u11f9" + + "\u1e00\u1e9b\u1ea0\u1ef9\u1f00\u1f15\u1f18\u1f1d" + + "\u1f20\u1f45\u1f48\u1f4d\u1f50\u1f57\u1f59\u1f59" + + "\u1f5b\u1f5b\u1f5d\u1f5d\u1f5f\u1f7d\u1f80\u1fb4" + + "\u1fb6\u1fbc\u1fbe\u1fbe\u1fc2\u1fc4\u1fc6\u1fcc" + + "\u1fd0\u1fd3\u1fd6\u1fdb\u1fe0\u1fec\u1ff2\u1ff4" + + "\u1ff6\u1ffc\u2126\u2126\u212a\u212b\u212e\u212e" + + "\u2180\u2182\u3007\u3007\u3021\u3029\u3041\u3094" + + "\u30a1\u30fa\u3105\u312c\u4e00\u9fa5\uac00\ud7a3"; + + const string s_NCNameXml4e = + "\u002d\u002e\u0030\u0039\u0041\u005a\u005f\u005f" + + "\u0061\u007a\u00b7\u00b7\u00c0\u00d6\u00d8\u00f6" + + "\u00f8\u0131\u0134\u013e\u0141\u0148\u014a\u017e" + + "\u0180\u01c3\u01cd\u01f0\u01f4\u01f5\u01fa\u0217" + + "\u0250\u02a8\u02bb\u02c1\u02d0\u02d1\u0300\u0345" + + "\u0360\u0361\u0386\u038a\u038c\u038c\u038e\u03a1" + + "\u03a3\u03ce\u03d0\u03d6\u03da\u03da\u03dc\u03dc" + + "\u03de\u03de\u03e0\u03e0\u03e2\u03f3\u0401\u040c" + + "\u040e\u044f\u0451\u045c\u045e\u0481\u0483\u0486" + + "\u0490\u04c4\u04c7\u04c8\u04cb\u04cc\u04d0\u04eb" + + "\u04ee\u04f5\u04f8\u04f9\u0531\u0556\u0559\u0559" + + "\u0561\u0586\u0591\u05a1\u05a3\u05b9\u05bb\u05bd" + + "\u05bf\u05bf\u05c1\u05c2\u05c4\u05c4\u05d0\u05ea" + + "\u05f0\u05f2\u0621\u063a\u0640\u0652\u0660\u0669" + + "\u0670\u06b7\u06ba\u06be\u06c0\u06ce\u06d0\u06d3" + + "\u06d5\u06e8\u06ea\u06ed\u06f0\u06f9\u0901\u0903" + + "\u0905\u0939\u093c\u094d\u0951\u0954\u0958\u0963" + + "\u0966\u096f\u0981\u0983\u0985\u098c\u098f\u0990" + + "\u0993\u09a8\u09aa\u09b0\u09b2\u09b2\u09b6\u09b9" + + "\u09bc\u09bc\u09be\u09c4\u09c7\u09c8\u09cb\u09cd" + + "\u09d7\u09d7\u09dc\u09dd\u09df\u09e3\u09e6\u09f1" + + "\u0a02\u0a02\u0a05\u0a0a\u0a0f\u0a10\u0a13\u0a28" + + "\u0a2a\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39" + + "\u0a3c\u0a3c\u0a3e\u0a42\u0a47\u0a48\u0a4b\u0a4d" + + "\u0a59\u0a5c\u0a5e\u0a5e\u0a66\u0a74\u0a81\u0a83" + + "\u0a85\u0a8b\u0a8d\u0a8d\u0a8f\u0a91\u0a93\u0aa8" + + "\u0aaa\u0ab0\u0ab2\u0ab3\u0ab5\u0ab9\u0abc\u0ac5" + + "\u0ac7\u0ac9\u0acb\u0acd\u0ae0\u0ae0\u0ae6\u0aef" + + "\u0b01\u0b03\u0b05\u0b0c\u0b0f\u0b10\u0b13\u0b28" + + "\u0b2a\u0b30\u0b32\u0b33\u0b36\u0b39\u0b3c\u0b43" + + "\u0b47\u0b48\u0b4b\u0b4d\u0b56\u0b57\u0b5c\u0b5d" + + "\u0b5f\u0b61\u0b66\u0b6f\u0b82\u0b83\u0b85\u0b8a" + + "\u0b8e\u0b90\u0b92\u0b95\u0b99\u0b9a\u0b9c\u0b9c" + + "\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0baa\u0bae\u0bb5" + + "\u0bb7\u0bb9\u0bbe\u0bc2\u0bc6\u0bc8\u0bca\u0bcd" + + "\u0bd7\u0bd7\u0be7\u0bef\u0c01\u0c03\u0c05\u0c0c" + + "\u0c0e\u0c10\u0c12\u0c28\u0c2a\u0c33\u0c35\u0c39" + + "\u0c3e\u0c44\u0c46\u0c48\u0c4a\u0c4d\u0c55\u0c56" + + "\u0c60\u0c61\u0c66\u0c6f\u0c82\u0c83\u0c85\u0c8c" + + "\u0c8e\u0c90\u0c92\u0ca8\u0caa\u0cb3\u0cb5\u0cb9" + + "\u0cbe\u0cc4\u0cc6\u0cc8\u0cca\u0ccd\u0cd5\u0cd6" + + "\u0cde\u0cde\u0ce0\u0ce1\u0ce6\u0cef\u0d02\u0d03" + + "\u0d05\u0d0c\u0d0e\u0d10\u0d12\u0d28\u0d2a\u0d39" + + "\u0d3e\u0d43\u0d46\u0d48\u0d4a\u0d4d\u0d57\u0d57" + + "\u0d60\u0d61\u0d66\u0d6f\u0e01\u0e2e\u0e30\u0e3a" + + "\u0e40\u0e4e\u0e50\u0e59\u0e81\u0e82\u0e84\u0e84" + + "\u0e87\u0e88\u0e8a\u0e8a\u0e8d\u0e8d\u0e94\u0e97" + + "\u0e99\u0e9f\u0ea1\u0ea3\u0ea5\u0ea5\u0ea7\u0ea7" + + "\u0eaa\u0eab\u0ead\u0eae\u0eb0\u0eb9\u0ebb\u0ebd" + + "\u0ec0\u0ec4\u0ec6\u0ec6\u0ec8\u0ecd\u0ed0\u0ed9" + + "\u0f18\u0f19\u0f20\u0f29\u0f35\u0f35\u0f37\u0f37" + + "\u0f39\u0f39\u0f3e\u0f47\u0f49\u0f69\u0f71\u0f84" + + "\u0f86\u0f8b\u0f90\u0f95\u0f97\u0f97\u0f99\u0fad" + + "\u0fb1\u0fb7\u0fb9\u0fb9\u10a0\u10c5\u10d0\u10f6" + + "\u1100\u1100\u1102\u1103\u1105\u1107\u1109\u1109" + + "\u110b\u110c\u110e\u1112\u113c\u113c\u113e\u113e" + + "\u1140\u1140\u114c\u114c\u114e\u114e\u1150\u1150" + + "\u1154\u1155\u1159\u1159\u115f\u1161\u1163\u1163" + + "\u1165\u1165\u1167\u1167\u1169\u1169\u116d\u116e" + + "\u1172\u1173\u1175\u1175\u119e\u119e\u11a8\u11a8" + + "\u11ab\u11ab\u11ae\u11af\u11b7\u11b8\u11ba\u11ba" + + "\u11bc\u11c2\u11eb\u11eb\u11f0\u11f0\u11f9\u11f9" + + "\u1e00\u1e9b\u1ea0\u1ef9\u1f00\u1f15\u1f18\u1f1d" + + "\u1f20\u1f45\u1f48\u1f4d\u1f50\u1f57\u1f59\u1f59" + + "\u1f5b\u1f5b\u1f5d\u1f5d\u1f5f\u1f7d\u1f80\u1fb4" + + "\u1fb6\u1fbc\u1fbe\u1fbe\u1fc2\u1fc4\u1fc6\u1fcc" + + "\u1fd0\u1fd3\u1fd6\u1fdb\u1fe0\u1fec\u1ff2\u1ff4" + + "\u1ff6\u1ffc\u20d0\u20dc\u20e1\u20e1\u2126\u2126" + + "\u212a\u212b\u212e\u212e\u2180\u2182\u3005\u3005" + + "\u3007\u3007\u3021\u302f\u3031\u3035\u3041\u3094" + + "\u3099\u309a\u309d\u309e\u30a1\u30fa\u30fc\u30fe" + + "\u3105\u312c\u4e00\u9fa5\uac00\ud7a3"; +#endif + + // static lock for XmlCharType class + private static object s_Lock; + + private static object StaticLock { + get { + if ( s_Lock == null ) { + object o = new object(); + Interlocked.CompareExchange( ref s_Lock, o, null ); + } + return s_Lock; + } + } + +#if XMLCHARTYPE_USE_RESOURCE + +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY + [System.Security.SecurityCritical] +#endif + private static volatile byte* s_CharProperties; + +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY + [System.Security.SecurityCritical] +#endif + internal byte* charProperties; + +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY + [System.Security.SecurityCritical] +#endif + static void InitInstance() { + lock ( StaticLock ) { + if ( s_CharProperties != null ) { + return; + } + + UnmanagedMemoryStream memStream = (UnmanagedMemoryStream)Assembly.GetExecutingAssembly().GetManifestResourceStream( "XmlCharType.bin" ); + Debug.Assert( memStream.Length == CharPropertiesSize ); + + byte* chProps = memStream.PositionPointer; + Thread.MemoryBarrier(); // For weak memory models (IA64) + s_CharProperties = chProps; + } + } + +#else // !XMLCHARTYPE_USE_RESOURCE + private static volatile byte [] s_CharProperties; + internal byte [] charProperties; + + static void InitInstance() { + lock ( StaticLock ) { + if ( s_CharProperties != null ) { + return; + } + + byte[] chProps = new byte[CharPropertiesSize]; + s_CharProperties = chProps; + + SetProperties( s_Whitespace, fWhitespace ); + SetProperties( s_LetterXml4e, fLetter ); + SetProperties( s_NCStartName, fNCStartNameSC ); + SetProperties( s_NCName, fNCNameSC ); + SetProperties( s_CharData, fCharData ); + SetProperties( s_NCNameXml4e, fNCNameXml4e ); + SetProperties( s_Text, fText ); + SetProperties( s_AttrValue, fAttrValue ); + } + } + + private static void SetProperties( string ranges, byte value ) { + for ( int p = 0; p < ranges.Length; p += 2 ) { + for ( int i = ranges[p], last = ranges[p + 1]; i <= last; i++ ) { + s_CharProperties[i] |= value; + } + } + } +#endif + +#if XMLCHARTYPE_USE_RESOURCE +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY + [System.Security.SecurityCritical] +#endif + private XmlCharType( byte* charProperties ) { +#else + private XmlCharType( byte[] charProperties ) { +#endif + Debug.Assert( s_CharProperties != null ); + this.charProperties = charProperties; + } + + public static XmlCharType Instance { +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + get { + if ( s_CharProperties == null ) { + InitInstance(); + } + return new XmlCharType( s_CharProperties ); + } + } + + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsWhiteSpace( char ch ) { + return ( charProperties[ch] & fWhitespace ) != 0; + } + +#if !SILVERLIGHT + public bool IsExtender( char ch ) { + return ( ch == 0xb7 ); + } +#endif + + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsNCNameSingleChar(char ch) { + return ( charProperties[ch] & fNCNameSC ) != 0; + } + +#if XML10_FIFTH_EDITION + public bool IsNCNameSurrogateChar( string str, int index ) { + if ( index + 1 >= str.Length ) { + return false; + } + return InRange( str[index], s_NCNameSurHighStart, s_NCNameSurHighEnd ) && + InRange( str[index + 1], s_NCNameSurLowStart, s_NCNameSurLowEnd ); + } + + // Surrogate characters for names are the same for NameChar and StartNameChar, + // so this method can be used for both + public bool IsNCNameSurrogateChar(char lowChar, char highChar) { + return InRange( highChar, s_NCNameSurHighStart, s_NCNameSurHighEnd ) && + InRange( lowChar, s_NCNameSurLowStart, s_NCNameSurLowEnd ); + } + + public bool IsNCNameHighSurrogateChar( char highChar ) { + return InRange( highChar, s_NCNameSurHighStart, s_NCNameSurHighEnd ); + } + + public bool IsNCNameLowSurrogateChar( char lowChar ) { + return InRange( lowChar, s_NCNameSurLowStart, s_NCNameSurLowEnd ); + } +#endif + + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsStartNCNameSingleChar(char ch) { + return ( charProperties[ch] & fNCStartNameSC ) != 0; + } + +#if XML10_FIFTH_EDITION + // !!! NOTE: These is no IsStartNCNameSurrogateChar, use IsNCNameSurrogateChar instead. + // Surrogate ranges for start name charaters are the same as for name characters. +#endif + + public bool IsNameSingleChar(char ch) { + return IsNCNameSingleChar(ch) || ch == ':'; + } + +#if XML10_FIFTH_EDITION + public bool IsNameSurrogateChar(char lowChar, char highChar) { + return IsNCNameSurrogateChar(lowChar, highChar); + } +#endif + + public bool IsStartNameSingleChar(char ch) { + return IsStartNCNameSingleChar(ch) || ch == ':'; + } + + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsCharData( char ch ) { + return ( charProperties[ch] & fCharData ) != 0; + } + + // [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] Section 2.3 of spec +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsPubidChar( char ch ) { + if ( ch < (char)0x80 ) { + return ( s_PublicIdBitmap[ch >> 4] & ( 1 << ( ch & 0xF ) ) ) != 0; + } + return false; + } + + // TextChar = CharData - { 0xA, 0xD, '<', '&', ']' } + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal bool IsTextChar( char ch ) { + return ( charProperties[ch] & fText ) != 0; + } + + // AttrValueChar = CharData - { 0xA, 0xD, 0x9, '<', '>', '&', '\'', '"' } + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal bool IsAttributeValueChar( char ch ) { + return ( charProperties[ch] & fAttrValue ) != 0; + } + + // XML 1.0 Fourth Edition definitions + // + // NOTE: This method will not be inlined (because it uses byte* charProperties) +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsLetter( char ch ) { + return ( charProperties[ch] & fLetter ) != 0; + } + + // NOTE: This method will not be inlined (because it uses byte* charProperties) + // This method uses the XML 4th edition name character ranges +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + public bool IsNCNameCharXml4e( char ch ) { + return ( charProperties[ch] & fNCNameXml4e ) != 0; + } + + // This method uses the XML 4th edition name character ranges + public bool IsStartNCNameCharXml4e( char ch ) { + return IsLetter( ch ) || ch == '_'; + } + + // This method uses the XML 4th edition name character ranges + public bool IsNameCharXml4e( char ch ) { + return IsNCNameCharXml4e( ch ) || ch == ':'; + } + + // This method uses the XML 4th edition name character ranges + public bool IsStartNameCharXml4e( char ch ) { + return IsStartNCNameCharXml4e( ch ) || ch == ':'; + } + + // Digit methods + public static bool IsDigit( char ch ) { + return InRange( ch, 0x30, 0x39 ); + } + +#if !SILVERLIGHT + public static bool IsHexDigit(char ch) { + return InRange( ch, 0x30, 0x39 ) || InRange( ch, 'a', 'f' ) || InRange( ch, 'A', 'F' ); + } +#endif + + // Surrogate methods + internal static bool IsHighSurrogate( int ch ) { + return InRange( ch, SurHighStart, SurHighEnd ); + } + + internal static bool IsLowSurrogate( int ch ) { + return InRange( ch, SurLowStart, SurLowEnd ); + } + + internal static bool IsSurrogate( int ch ) { + return InRange( ch, SurHighStart, SurLowEnd ); + } + + internal static int CombineSurrogateChar( int lowChar, int highChar ) { + return ( lowChar - SurLowStart ) | ( ( highChar - SurHighStart ) << 10 ) + 0x10000; + } + + internal static void SplitSurrogateChar( int combinedChar, out char lowChar, out char highChar ) { + int v = combinedChar - 0x10000; + lowChar = (char)( SurLowStart + v % 1024 ); + highChar = (char)( SurHighStart + v / 1024 ); + } + + internal bool IsOnlyWhitespace( string str ) { + return IsOnlyWhitespaceWithPos( str ) == -1; + } + + // Character checking on strings +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal int IsOnlyWhitespaceWithPos( string str ) { + if ( str != null ) { + for ( int i = 0; i < str.Length; i++ ) { + if ( ( charProperties[str[i]] & fWhitespace ) == 0 ) { + return i; + } + } + } + return -1; + } + +#if SILVERLIGHT && !SILVERLIGHT_DISABLE_SECURITY && XMLCHARTYPE_USE_RESOURCE + [System.Security.SecuritySafeCritical] +#endif + internal int IsOnlyCharData( string str ) { + if ( str != null ) { + for ( int i = 0; i < str.Length; i++ ) { + if ( ( charProperties[str[i]] & fCharData ) == 0 ) { + if ( i + 1 >= str.Length || !(XmlCharType.IsHighSurrogate(str[i]) && XmlCharType.IsLowSurrogate(str[i+1]))) { + return i; + } + else { + i++; + } + } + } + } + return -1; + } + + static internal bool IsOnlyDigits(string str, int startPos, int len) { + Debug.Assert(str != null); + Debug.Assert(startPos + len <= str.Length); + Debug.Assert(startPos <= str.Length); + + for (int i = startPos; i < startPos + len; i++) { + if (!IsDigit(str[i])) { + return false; + } + } + return true; + } + + static internal bool IsOnlyDigits( char[] chars, int startPos, int len ) { + Debug.Assert( chars != null ); + Debug.Assert( startPos + len <= chars.Length ); + Debug.Assert( startPos <= chars.Length ); + + for ( int i = startPos; i < startPos + len; i++ ) { + if ( !IsDigit( chars[i] ) ) { + return false; + } + } + return true; + } + + internal int IsPublicId( string str ) { + if ( str != null ) { + for ( int i = 0; i < str.Length; i++ ) { + if ( !IsPubidChar(str[i]) ) { + return i; + } + } + } + return -1; + } + + // This method tests whether a value is in a given range with just one test; start and end should be constants + private static bool InRange(int value, int start, int end) { + Debug.Assert(start <= end); + return (uint)(value - start) <= (uint)(end - start); + } + +#if XMLCHARTYPE_GEN_RESOURCE + // + // Code for generating XmlCharType.bin table and s_PublicIdBitmap + // + // build command line: csc XmlCharType.cs /d:XMLCHARTYPE_GEN_RESOURCE + // + public static void Main( string[] args ) { + try { + InitInstance(); + + // generate PublicId bitmap + ushort[] bitmap = new ushort[0x80 >> 4]; + for (int i = 0; i < s_PublicID.Length; i += 2) { + for (int j = s_PublicID[i], last = s_PublicID[i + 1]; j <= last; j++) { + bitmap[j >> 4] |= (ushort)(1 << (j & 0xF)); + } + } + + Console.Write("private const string s_PublicIdBitmap = \""); + for (int i = 0; i < bitmap.Length; i++) { + Console.Write("\\u{0:x4}", bitmap[i]); + } + Console.WriteLine("\";"); + Console.WriteLine(); + + string fileName = ( args.Length == 0 ) ? "XmlCharType.bin" : args[0]; + Console.Write( "Writing XmlCharType character properties to {0}...", fileName ); + + FileStream fs = new FileStream( fileName, FileMode.Create ); + for ( int i = 0; i < CharPropertiesSize; i += 4096 ) { + fs.Write( s_CharProperties, i, 4096 ); + } + fs.Close(); + Console.WriteLine( "done." ); + } + catch ( Exception e ) { + Console.WriteLine(); + Console.WriteLine( "Exception: {0}", e.Message ); + } + } +#endif + } +} diff --git a/src/Shared/Compat/XmlReservedNs.cs b/src/Shared/Compat/XmlReservedNs.cs new file mode 100644 index 00000000000..4564ecf7a6c --- /dev/null +++ b/src/Shared/Compat/XmlReservedNs.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +namespace System.Xml { + + /// + /// This class defines a set of common XML namespaces for sharing across multiple source files. + /// + internal static class XmlReservedNs { + internal const string NsXml = "http://www.w3.org/XML/1998/namespace"; + internal const string NsXmlNs = "http://www.w3.org/2000/xmlns/"; +#if !SILVERLIGHT // These strings are not needed in Silverlight + internal const string NsDataType = "urn:schemas-microsoft-com:datatypes"; + internal const string NsDataTypeAlias = "uuid:C2F41010-65B3-11D1-A29F-00AA00C14882"; + internal const string NsDataTypeOld = "urn:uuid:C2F41010-65B3-11D1-A29F-00AA00C14882/"; + internal const string NsMsxsl = "urn:schemas-microsoft-com:xslt"; + internal const string NsXdr = "urn:schemas-microsoft-com:xml-data"; + internal const string NsXslDebug = "urn:schemas-microsoft-com:xslt-debug"; + internal const string NsXdrAlias = "uuid:BDC6E3F0-6DA3-11D1-A2A3-00AA00C14882"; + internal const string NsWdXsl = "http://www.w3.org/TR/WD-xsl"; + internal const string NsXs = "http://www.w3.org/2001/XMLSchema"; + internal const string NsXsd = "http://www.w3.org/2001/XMLSchema-datatypes"; + internal const string NsXsi = "http://www.w3.org/2001/XMLSchema-instance"; + internal const string NsXslt = "http://www.w3.org/1999/XSL/Transform"; + internal const string NsExsltCommon = "http://exslt.org/common"; + internal const string NsExsltDates = "http://exslt.org/dates-and-times"; + internal const string NsExsltMath = "http://exslt.org/math"; + internal const string NsExsltRegExps = "http://exslt.org/regular-expressions"; + internal const string NsExsltSets = "http://exslt.org/sets"; + internal const string NsExsltStrings = "http://exslt.org/strings"; + internal const string NsXQueryFunc = "http://www.w3.org/2003/11/xpath-functions"; + internal const string NsXQueryDataType = "http://www.w3.org/2003/11/xpath-datatypes"; + internal const string NsCollationBase = "http://collations.microsoft.com"; + internal const string NsCollCodePoint = "http://www.w3.org/2004/10/xpath-functions/collation/codepoint"; + internal const string NsXsltInternal = "http://schemas.microsoft.com/framework/2003/xml/xslt/internal"; +#endif + }; +} diff --git a/src/Shared/Compat/XmlTextEncoder.cs b/src/Shared/Compat/XmlTextEncoder.cs new file mode 100644 index 00000000000..b84f848981b --- /dev/null +++ b/src/Shared/Compat/XmlTextEncoder.cs @@ -0,0 +1,504 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Globalization; + +namespace System.Xml { + + // XmlTextEncoder + // + // This class does special handling of text content for XML. For example + // it will replace special characters with entities whenever necessary. + internal class XmlTextEncoder { +// +// Fields +// + // output text writer + TextWriter textWriter; + + // true when writing out the content of attribute value + bool inAttribute; + + // quote char of the attribute (when inAttribute) + char quoteChar; + + // caching of attribute value + StringBuilder attrValue; + bool cacheAttrValue; + + // XmlCharType + XmlCharType xmlCharType; + +// +// Constructor +// + internal XmlTextEncoder( TextWriter textWriter ) { + this.textWriter = textWriter; + this.quoteChar = '"'; + this.xmlCharType = XmlCharType.Instance; + } + +// +// Internal methods and properties +// + internal char QuoteChar { + set { + this.quoteChar = value; + } + } + + internal void StartAttribute( bool cacheAttrValue ) { + this.inAttribute = true; + this.cacheAttrValue = cacheAttrValue; + if ( cacheAttrValue ) { + if ( attrValue == null ) { + attrValue = new StringBuilder(); + } + else { + attrValue.Length = 0; + } + } + } + + internal void EndAttribute() { + if ( cacheAttrValue ) { + attrValue.Length = 0; + } + this.inAttribute = false; + this.cacheAttrValue = false; + } + + internal string AttributeValue { + get { + if ( cacheAttrValue ) { + return attrValue.ToString(); + } + else { + return String.Empty; + } + } + } + + internal void WriteSurrogateChar( char lowChar, char highChar ) { + if ( !XmlCharType.IsLowSurrogate(lowChar) || + !XmlCharType.IsHighSurrogate( highChar ) ) { + throw CreateInvalidSurrogatePairException( lowChar, highChar ); + } + + textWriter.Write( highChar ); + textWriter.Write( lowChar ); + } + +#if FEATURE_NETCORE + [System.Security.SecurityCritical] +#endif + internal void Write( char[] array, int offset, int count ) { + if ( null == array ) { + throw new ArgumentNullException("array"); + } + + if ( 0 > offset ) { + throw new ArgumentOutOfRangeException("offset"); + } + + if ( 0 > count ) { + throw new ArgumentOutOfRangeException("count"); + } + + if ( count > array.Length - offset ) { + throw new ArgumentOutOfRangeException("count"); + } + + if ( cacheAttrValue ) { + attrValue.Append( array, offset, count ); + } + + int endPos = offset + count; + int i = offset; + char ch = (char)0; + for (;;) { + int startPos = i; + unsafe { + while ( i < endPos && ( xmlCharType.charProperties[ch = array[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( ch = array[i] ) ) ) ) { + i++; + } + } + + if ( startPos < i ) { + textWriter.Write( array, startPos, i - startPos ); + } + if ( i == endPos ) { + break; + } + + switch ( ch ) { + case (char)0x9: + textWriter.Write( ch ); + break; + case (char)0xA: + case (char)0xD: + if ( inAttribute ) { + WriteCharEntityImpl( ch ); + } + else { + textWriter.Write( ch ); + } + break; + + case '<': + WriteEntityRefImpl( "lt" ); + break; + case '>': + WriteEntityRefImpl( "gt" ); + break; + case '&': + WriteEntityRefImpl( "amp" ); + break; + case '\'': + if ( inAttribute && quoteChar == ch ) { + WriteEntityRefImpl( "apos" ); + } + else { + textWriter.Write( '\'' ); + } + break; + case '"': + if ( inAttribute && quoteChar == ch ) { + WriteEntityRefImpl( "quot" ); + } + else { + textWriter.Write( '"' ); + } + break; + default: + if ( XmlCharType.IsHighSurrogate( ch ) ) { + if ( i + 1 < endPos ) { + WriteSurrogateChar( array[++i], ch ); + } + else { + throw new ArgumentException( "Xml_SurrogatePairSplit" ); + } + } + else if ( XmlCharType.IsLowSurrogate( ch ) ) { + throw CreateInvalidHighSurrogateCharException( ch ); + } + else { + Debug.Assert( ( ch < 0x20 && !xmlCharType.IsWhiteSpace( ch ) ) || ( ch > 0xFFFD ) ); + WriteCharEntityImpl( ch ); + } + break; + } + i++; + } + } + + internal void WriteSurrogateCharEntity( char lowChar, char highChar ) { + if ( !XmlCharType.IsLowSurrogate( lowChar ) || + !XmlCharType.IsHighSurrogate( highChar ) ) { + throw CreateInvalidSurrogatePairException( lowChar, highChar ); + + } + int surrogateChar = XmlCharType.CombineSurrogateChar( lowChar, highChar ); + + if ( cacheAttrValue ) { + attrValue.Append( highChar ); + attrValue.Append( lowChar ); + } + + textWriter.Write( "&#x" ); + textWriter.Write( surrogateChar.ToString( "X", NumberFormatInfo.InvariantInfo ) ); + textWriter.Write( ';' ); + } + +#if FEATURE_NETCORE + [System.Security.SecurityCritical] +#endif + internal void Write( string text ) { + if ( text == null ) { + return; + } + + if ( cacheAttrValue ) { + attrValue.Append( text ); + } + + // scan through the string to see if there are any characters to be escaped + int len = text.Length; + int i = 0; + int startPos = 0; + char ch = (char)0; + for (;;) { + unsafe { + while ( i < len && ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( ch = text[i] ) ) ) ) { + i++; + } + } + if ( i == len ) { + // reached the end of the string -> write it whole out + textWriter.Write( text ); + return; + } + if ( inAttribute ) { + if ( ch == 0x9 ) { + i++; + continue; + } + } + else { + if ( ch == 0x9 || ch == 0xA || ch == 0xD || ch == '"' || ch == '\'' ) { + i++; + continue; + } + } + // some character that needs to be escaped is found: + break; + } + + char[] helperBuffer = new char[256]; + for (;;) { + if ( startPos < i ) { + WriteStringFragment( text, startPos, i - startPos, helperBuffer ); + } + if ( i == len ) { + break; + } + + switch ( ch ) { + case (char)0x9: + textWriter.Write( ch ); + break; + case (char)0xA: + case (char)0xD: + if ( inAttribute ) { + WriteCharEntityImpl( ch ); + } + else { + textWriter.Write( ch ); + } + break; + case '<': + WriteEntityRefImpl( "lt" ); + break; + case '>': + WriteEntityRefImpl( "gt" ); + break; + case '&': + WriteEntityRefImpl( "amp" ); + break; + case '\'': + if ( inAttribute && quoteChar == ch ) { + WriteEntityRefImpl( "apos" ); + } + else { + textWriter.Write( '\'' ); + } + break; + case '"': + if ( inAttribute && quoteChar == ch ) { + WriteEntityRefImpl( "quot" ); + } + else { + textWriter.Write( '"' ); + } + break; + default: + if ( XmlCharType.IsHighSurrogate( ch ) ) { + if ( i + 1 < len ) { + WriteSurrogateChar( text[++i], ch ); + } + else { + throw CreateInvalidSurrogatePairException( text[i], ch ); + } + } + else if ( XmlCharType.IsLowSurrogate( ch ) ) { + throw CreateInvalidHighSurrogateCharException( ch ); + } + else { + Debug.Assert( ( ch < 0x20 && !xmlCharType.IsWhiteSpace( ch ) ) || ( ch > 0xFFFD ) ); + WriteCharEntityImpl( ch ); + } + break; + } + i++; + startPos = i; + unsafe { + while ( i < len && ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( text[i] ) ) ) ) { + i++; + } + } + } + } + +#if FEATURE_NETCORE + [System.Security.SecurityCritical] +#endif + internal void WriteRawWithSurrogateChecking( string text ) { + if ( text == null ) { + return; + } + if ( cacheAttrValue ) { + attrValue.Append( text ); + } + + int len = text.Length; + int i = 0; + char ch = (char)0; + + for (;;) { + unsafe { + while ( i < len && + ( ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fCharData ) != 0 // ( xmlCharType.IsCharData( ( ch = text[i] ) ) + || ch < 0x20 ) ) { + i++; + } + } + if ( i == len ) { + break; + } + if ( XmlCharType.IsHighSurrogate( ch ) ) { + if ( i + 1 < len ) { + char lowChar = text[i+1]; + if ( XmlCharType.IsLowSurrogate( lowChar ) ) { + i += 2; + continue; + } + else { + throw CreateInvalidSurrogatePairException( lowChar, ch ); + } + } + throw new ArgumentException( "Xml_InvalidSurrogateMissingLowChar" ); + } + else if ( XmlCharType.IsLowSurrogate( ch ) ) { + throw CreateInvalidHighSurrogateCharException( ch ); + } + else { + i++; + } + } + + textWriter.Write( text ); + return; + } + + internal void WriteRaw( string value ) { + if ( cacheAttrValue ) { + attrValue.Append( value ); + } + textWriter.Write( value ); + } + + internal void WriteRaw( char[] array, int offset, int count ) { + if ( null == array ) { + throw new ArgumentNullException("array"); + } + + if ( 0 > count ) { + throw new ArgumentOutOfRangeException("count"); + } + + if ( 0 > offset ) { + throw new ArgumentOutOfRangeException("offset"); + } + + if ( count > array.Length - offset ) { + throw new ArgumentOutOfRangeException("count"); + } + + if ( cacheAttrValue ) { + attrValue.Append( array, offset, count ); + } + textWriter.Write( array, offset, count ); + } + + + + internal void WriteCharEntity( char ch ) { + if ( XmlCharType.IsSurrogate(ch) ) { + throw new ArgumentException( "Xml_InvalidSurrogateMissingLowChar" ); + } + + string strVal = ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo ); + if ( cacheAttrValue ) { + attrValue.Append( "&#x" ); + attrValue.Append( strVal ); + attrValue.Append( ';' ); + } + WriteCharEntityImpl( strVal ); + } + + internal void WriteEntityRef( string name ) { + if ( cacheAttrValue ) { + attrValue.Append( '&' ); + attrValue.Append( name ); + attrValue.Append( ';' ); + } + WriteEntityRefImpl( name ); + } + + internal void Flush() { + // + } + +// +// Private implementation methods +// + // This is a helper method to woraround the fact that TextWriter does not have a Write method + // for fragment of a string such as Write( string, offset, count). + // The string fragment will be written out by copying into a small helper buffer and then + // calling textWriter to write out the buffer. + private void WriteStringFragment( string str, int offset, int count, char[] helperBuffer ) { + int bufferSize = helperBuffer.Length; + while ( count > 0 ) { + int copyCount = count; + if ( copyCount > bufferSize ) { + copyCount = bufferSize; + } + + str.CopyTo( offset, helperBuffer, 0, copyCount ); + textWriter.Write( helperBuffer, 0, copyCount ); + offset += copyCount; + count -= copyCount; + } + } + + private void WriteCharEntityImpl( char ch ) { + WriteCharEntityImpl( ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo ) ); + } + + private void WriteCharEntityImpl( string strVal ) { + textWriter.Write( "&#x" ); + textWriter.Write( strVal ); + textWriter.Write( ';' ); + } + + private void WriteEntityRefImpl( string name ) { + textWriter.Write( '&' ); + textWriter.Write( name ); + textWriter.Write( ';' ); + } + + // Exception methods from XmlConvert which weren't in .NET Core + internal static Exception CreateInvalidSurrogatePairException( char low, char hi ) { + + string[] args = new string[] { + ((uint)hi).ToString( "X", CultureInfo.InvariantCulture ), + ((uint)low).ToString( "X", CultureInfo.InvariantCulture ) + }; + + return new ArgumentException(string.Format("The surrogate pair (0x{0}, 0x{1}) is invalid. A high surrogate character (0xD800 - 0xDBFF) must always be paired with a low surrogate character (0xDC00 - 0xDFFF).", args)); + } + + internal static Exception CreateInvalidHighSurrogateCharException( char hi ) { + return new ArgumentException(string.Format("Invalid high surrogate character (0x{0}). A high surrogate character must have a value from range (0xD800 - 0xDBFF).", + ((uint)hi).ToString("X", CultureInfo.InvariantCulture))); + } + } +} diff --git a/src/Shared/Compat/XmlTextWriter.cs b/src/Shared/Compat/XmlTextWriter.cs new file mode 100644 index 00000000000..89523b62328 --- /dev/null +++ b/src/Shared/Compat/XmlTextWriter.cs @@ -0,0 +1,1484 @@ +//------------------------------------------------------------------------------ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// [....] +//------------------------------------------------------------------------------ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.Versioning; + +namespace System.Xml { + + // Specifies formatting options for XmlTextWriter. + public enum Formatting { + // No special formatting is done (this is the default). + None, + + //This option causes child elements to be indented using the Indentation and IndentChar properties. + // It only indents Element Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-element-content) + // and not Mixed Content (http://www.w3.org/TR/1998/REC-xml-19980210#sec-mixed-content) + // according to the XML 1.0 definitions of these terms. + Indented, + }; + + // Represents a writer that provides fast non-cached forward-only way of generating XML streams + // containing XML documents that conform to the W3CExtensible Markup Language (XML) 1.0 specification + // and the Namespaces in XML specification. + + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public class XmlTextWriter : XmlWriter { +// +// Private types +// + enum NamespaceState { + Uninitialized, + NotDeclaredButInScope, + DeclaredButNotWrittenOut, + DeclaredAndWrittenOut + } + + struct TagInfo { + internal string name; + internal string prefix; + internal string defaultNs; + internal NamespaceState defaultNsState; + internal XmlSpace xmlSpace; + internal string xmlLang; + internal int prevNsTop; + internal int prefixCount; + internal bool mixed; // whether to pretty print the contents of this element. + + internal void Init( int nsTop ) { + name = null; + defaultNs = String.Empty; + defaultNsState = NamespaceState.Uninitialized; + xmlSpace = XmlSpace.None; + xmlLang = null; + prevNsTop = nsTop; + prefixCount = 0; + mixed = false; + } + } + + struct Namespace { + internal string prefix; + internal string ns; + internal bool declared; + internal int prevNsIndex; + + internal void Set( string prefix, string ns, bool declared ) { + this.prefix = prefix; + this.ns = ns; + this.declared = declared; + this.prevNsIndex = -1; + } + } + + enum SpecialAttr { + None, + XmlSpace, + XmlLang, + XmlNs + }; + + // State machine is working through autocomplete + private enum State { + Start, + Prolog, + PostDTD, + Element, + Attribute, + Content, + AttrOnly, + Epilog, + Error, + Closed, + } + + private enum Token { + PI, + Doctype, + Comment, + CData, + StartElement, + EndElement, + LongEndElement, + StartAttribute, + EndAttribute, + Content, + Base64, + RawData, + Whitespace, + Empty + } + +// +// Fields +// + // output + TextWriter textWriter; + XmlTextEncoder xmlEncoder; + Encoding encoding; + + // formatting + Formatting formatting; + bool indented; // perf - faster to check a boolean. + int indentation; + char indentChar; + + // element stack + TagInfo[] stack; + int top; + + // state machine for AutoComplete + State[] stateTable; + State currentState; + Token lastToken; + + // Base64 content + XmlTextWriterBase64Encoder base64Encoder; + + // misc + char quoteChar; + char curQuoteChar; + bool namespaces; + SpecialAttr specialAttr; + string prefixForXmlNs; + bool flush; + + // namespaces + Namespace[] nsStack; + int nsTop; + Dictionary nsHashtable; + bool useNsHashtable; + + // char types + XmlCharType xmlCharType = XmlCharType.Instance; + +// +// Constants and constant tables +// + const int NamespaceStackInitialSize = 8; +#if DEBUG + const int MaxNamespacesWalkCount = 3; +#else + const int MaxNamespacesWalkCount = 16; +#endif + + static string[] stateName = { + "Start", + "Prolog", + "PostDTD", + "Element", + "Attribute", + "Content", + "AttrOnly", + "Epilog", + "Error", + "Closed", + }; + + static string[] tokenName = { + "PI", + "Doctype", + "Comment", + "CData", + "StartElement", + "EndElement", + "LongEndElement", + "StartAttribute", + "EndAttribute", + "Content", + "Base64", + "RawData", + "Whitespace", + "Empty" + }; + + static readonly State[] stateTableDefault = { + // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog + // + /* Token.PI */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, + /* Token.Doctype */ State.PostDTD, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, + /* Token.Comment */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, + /* Token.CData */ State.Content, State.Content, State.Error, State.Content, State.Content, State.Content, State.Error, State.Epilog, + /* Token.StartElement */ State.Element, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Element, + /* Token.EndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, + /* Token.LongEndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, + /* Token.StartAttribute */ State.AttrOnly, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error, + /* Token.EndAttribute */ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Epilog, State.Error, + /* Token.Content */ State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, + /* Token.Base64 */ State.Content, State.Content, State.Error, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, + /* Token.RawData */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, + /* Token.Whitespace */ State.Prolog, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Attribute, State.Epilog, + }; + + static readonly State[] stateTableDocument = { + // State.Start State.Prolog State.PostDTD State.Element State.Attribute State.Content State.AttrOnly State.Epilog + // + /* Token.PI */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, + /* Token.Doctype */ State.Error, State.PostDTD, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, + /* Token.Comment */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Content, State.Content, State.Error, State.Epilog, + /* Token.CData */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, + /* Token.StartElement */ State.Error, State.Element, State.Element, State.Element, State.Element, State.Element, State.Error, State.Error, + /* Token.EndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, + /* Token.LongEndElement */ State.Error, State.Error, State.Error, State.Content, State.Content, State.Content, State.Error, State.Error, + /* Token.StartAttribute */ State.Error, State.Error, State.Error, State.Attribute, State.Attribute, State.Error, State.Error, State.Error, + /* Token.EndAttribute */ State.Error, State.Error, State.Error, State.Error, State.Element, State.Error, State.Error, State.Error, + /* Token.Content */ State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error, + /* Token.Base64 */ State.Error, State.Error, State.Error, State.Content, State.Attribute, State.Content, State.Error, State.Error, + /* Token.RawData */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog, + /* Token.Whitespace */ State.Error, State.Prolog, State.PostDTD, State.Content, State.Attribute, State.Content, State.Error, State.Epilog, + }; + +// +// Constructors +// + internal XmlTextWriter() { + namespaces = true; + formatting = Formatting.None; + indentation = 2; + indentChar = ' '; + // namespaces + nsStack = new Namespace[NamespaceStackInitialSize]; + nsTop = -1; + // element stack + stack = new TagInfo[10]; + top = 0;// 0 is an empty sentanial element + stack[top].Init( -1 ); + quoteChar = '"'; + + stateTable = stateTableDefault; + currentState = State.Start; + lastToken = Token.Empty; + } + + // Creates an instance of the XmlTextWriter class using the specified stream. + public XmlTextWriter(Stream w, Encoding encoding) : this() { + this.encoding = encoding; + if (encoding != null) + textWriter = new StreamWriter(w, encoding); + else + textWriter = new StreamWriter(w); + xmlEncoder = new XmlTextEncoder(textWriter); + xmlEncoder.QuoteChar = this.quoteChar; + } + + // Creates an instance of the XmlTextWriter class using the specified file. + //[ResourceConsumption(ResourceScope.Machine)] + //[ResourceExposure(ResourceScope.Machine)] + public XmlTextWriter(String filename, Encoding encoding) + : this(new FileStream(filename, FileMode.Create, + FileAccess.Write, FileShare.Read), encoding) { + } + + // Creates an instance of the XmlTextWriter class using the specified TextWriter. + public XmlTextWriter(TextWriter w) : this() { + textWriter = w; + + encoding = w.Encoding; + xmlEncoder = new XmlTextEncoder(w); + xmlEncoder.QuoteChar = this.quoteChar; + } + +// +// XmlTextWriter properties +// + // Gets the XmlTextWriter base stream. + public Stream BaseStream { + get { + StreamWriter streamWriter = textWriter as StreamWriter; + return (streamWriter == null ? null : streamWriter.BaseStream); + } + } + + // Gets or sets a value indicating whether to do namespace support. + public bool Namespaces { + get { return this.namespaces;} + set { + if (this.currentState != State.Start) + throw new InvalidOperationException("Xml_NotInWriteState"); + + this.namespaces = value; + } + } + + // Indicates how the output is formatted. + public Formatting Formatting { + get { return this.formatting;} + set { this.formatting = value; this.indented = value == Formatting.Indented;} + } + + // Gets or sets how many IndentChars to write for each level in the hierarchy when Formatting is set to "Indented". + public int Indentation { + get { return this.indentation;} + set { + if (value < 0) + throw new ArgumentException("Xml_InvalidIndentation"); + this.indentation = value; + } + } + + // Gets or sets which character to use for indenting when Formatting is set to "Indented". + public char IndentChar { + get { return this.indentChar;} + set { this.indentChar = value;} + } + + // Gets or sets which character to use to quote attribute values. + public char QuoteChar { + get { return this.quoteChar;} + set { + if (value != '"' && value != '\'') { + throw new ArgumentException("Xml_InvalidQuote"); + } + this.quoteChar = value; + this.xmlEncoder.QuoteChar = value; + } + } + +// +// XmlWriter implementation +// + // Writes out the XML declaration with the version "1.0". + public override void WriteStartDocument() { + StartDocument(-1); + } + + // Writes out the XML declaration with the version "1.0" and the standalone attribute. + public override void WriteStartDocument(bool standalone) { + StartDocument(standalone ? 1 : 0); + } + + // Closes any open elements or attributes and puts the writer back in the Start state. + public override void WriteEndDocument() { + try { + AutoCompleteAll(); + if (this.currentState != State.Epilog) { + if (this.currentState == State.Closed) { + throw new ArgumentException("Xml_ClosedOrError"); + } + else { + throw new ArgumentException("Xml_NoRoot"); + } + } + this.stateTable = stateTableDefault; + this.currentState = State.Start; + this.lastToken = Token.Empty; + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the DOCTYPE declaration with the specified name and optional attributes. + public override void WriteDocType(string name, string pubid, string sysid, string subset) { + try { + ValidateName(name, false); + + AutoComplete(Token.Doctype); + textWriter.Write("'); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the specified start tag and associates it with the given namespace and prefix. + public override void WriteStartElement(string prefix, string localName, string ns) { + try { + AutoComplete(Token.StartElement); + PushStack(); + textWriter.Write('<'); + + if (this.namespaces) { + // Propagate default namespace and mix model down the stack. + stack[top].defaultNs = stack[top-1].defaultNs; + if (stack[top-1].defaultNsState != NamespaceState.Uninitialized) + stack[top].defaultNsState = NamespaceState.NotDeclaredButInScope; + stack[top].mixed = stack[top-1].mixed; + if (ns == null) { + // use defined prefix + if (prefix != null && prefix.Length != 0 && (LookupNamespace(prefix) == -1)) { + throw new ArgumentException("Xml_UndefPrefix"); + } + } + else { + if (prefix == null) { + string definedPrefix = FindPrefix(ns); + if (definedPrefix != null) { + prefix = definedPrefix; + } + else { + PushNamespace(null, ns, false); // new default + } + } + else if (prefix.Length == 0) { + PushNamespace(null, ns, false); // new default + } + else { + if (ns.Length == 0) { + prefix = null; + } + VerifyPrefixXml(prefix, ns); + PushNamespace(prefix, ns, false); // define + } + } + stack[top].prefix = null; + if (prefix != null && prefix.Length != 0) { + stack[top].prefix = prefix; + textWriter.Write(prefix); + textWriter.Write(':'); + } + } + else { + if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) { + throw new ArgumentException("Xml_NoNamespaces"); + } + } + stack[top].name = localName; + textWriter.Write(localName); + } + catch { + currentState = State.Error; + throw; + } + } + + // Closes one element and pops the corresponding namespace scope. + public override void WriteEndElement() { + InternalWriteEndElement(false); + } + + // Closes one element and pops the corresponding namespace scope. + public override void WriteFullEndElement() { + InternalWriteEndElement(true); + } + + // Writes the start of an attribute. + public override void WriteStartAttribute(string prefix, string localName, string ns) { + try { + AutoComplete(Token.StartAttribute); + + this.specialAttr = SpecialAttr.None; + if (this.namespaces) { + + if (prefix != null && prefix.Length == 0) { + prefix = null; + } + + if (ns == XmlReservedNs.NsXmlNs && prefix == null && localName != "xmlns") { + prefix = "xmlns"; + } + + if (prefix == "xml") { + if (localName == "lang") { + this.specialAttr = SpecialAttr.XmlLang; + } + else if (localName == "space") { + this.specialAttr = SpecialAttr.XmlSpace; + } + /* bug54408. to be fwd compatible we need to treat xml prefix as reserved + and not really insist on a specific value. Who knows in the future it + might be OK to say xml:blabla + else { + throw new ArgumentException(Res.GetString(Res.Xml_InvalidPrefix)); + }*/ + } + else if (prefix == "xmlns") { + + if (XmlReservedNs.NsXmlNs != ns && ns != null) { + throw new ArgumentException("Xml_XmlnsBelongsToReservedNs"); + } + if (localName == null || localName.Length == 0) { + localName = prefix; + prefix = null; + this.prefixForXmlNs = null; + } + else { + this.prefixForXmlNs = localName; + } + this.specialAttr = SpecialAttr.XmlNs; + } + else if (prefix == null && localName == "xmlns") { + if (XmlReservedNs.NsXmlNs != ns && ns != null) { + // add the below line back in when DOM is fixed + throw new ArgumentException("Xml_XmlnsBelongsToReservedNs"); + } + this.specialAttr = SpecialAttr.XmlNs; + this.prefixForXmlNs = null; + } + else { + if (ns == null) { + // use defined prefix + if (prefix != null && (LookupNamespace(prefix) == -1)) { + throw new ArgumentException("Xml_UndefPrefix"); + } + } + else if (ns.Length == 0) { + // empty namespace require null prefix + prefix = string.Empty; + } + else { // ns.Length != 0 + VerifyPrefixXml(prefix, ns); + if (prefix != null && LookupNamespaceInCurrentScope(prefix) != -1) { + prefix = null; + } + // Now verify prefix validity + string definedPrefix = FindPrefix(ns); + if (definedPrefix != null && (prefix == null || prefix == definedPrefix)) { + prefix = definedPrefix; + } + else { + if (prefix == null) { + prefix = GeneratePrefix(); // need a prefix if + } + PushNamespace(prefix, ns, false); + } + } + } + if (prefix != null && prefix.Length != 0) { + textWriter.Write(prefix); + textWriter.Write(':'); + } + } + else { + if ((ns != null && ns.Length != 0) || (prefix != null && prefix.Length != 0)) { + throw new ArgumentException("Xml_NoNamespaces"); + } + if (localName == "xml:lang") { + this.specialAttr = SpecialAttr.XmlLang; + } + else if (localName == "xml:space") { + this.specialAttr = SpecialAttr.XmlSpace; + } + } + xmlEncoder.StartAttribute(this.specialAttr != SpecialAttr.None); + + textWriter.Write(localName); + textWriter.Write('='); + if (this.curQuoteChar != this.quoteChar) { + this.curQuoteChar = this.quoteChar; + xmlEncoder.QuoteChar = this.quoteChar; + } + textWriter.Write(this.curQuoteChar); + } + catch { + currentState = State.Error; + throw; + } + } + + // Closes the attribute opened by WriteStartAttribute. + public override void WriteEndAttribute() { + try { + AutoComplete(Token.EndAttribute); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out a <![CDATA[...]]> block containing the specified text. + public override void WriteCData(string text) { + try { + AutoComplete(Token.CData); + if (null != text && text.IndexOf("]]>", StringComparison.Ordinal) >= 0) { + throw new ArgumentException("Xml_InvalidCDataChars"); + } + textWriter.Write(""); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out a comment containing the specified text. + public override void WriteComment(string text) { + try { + if (null != text && (text.IndexOf("--", StringComparison.Ordinal)>=0 || (text.Length != 0 && text[text.Length-1] == '-'))) { + throw new ArgumentException("Xml_InvalidCommentChars"); + } + AutoComplete(Token.Comment); + textWriter.Write(""); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out a processing instruction with a space between the name and text as follows: + public override void WriteProcessingInstruction(string name, string text) { + try { + if (null != text && text.IndexOf("?>", StringComparison.Ordinal)>=0) { + throw new ArgumentException("Xml_InvalidPiChars"); + } + if (0 == String.Compare(name, "xml", StringComparison.OrdinalIgnoreCase) && this.stateTable == stateTableDocument) { + throw new ArgumentException("Xml_DupXmlDecl"); + } + AutoComplete(Token.PI); + InternalWriteProcessingInstruction(name, text); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out an entity reference as follows: "&"+name+";". + public override void WriteEntityRef(string name) { + try { + ValidateName(name, false); + AutoComplete(Token.Content); + xmlEncoder.WriteEntityRef(name); + } + catch { + currentState = State.Error; + throw; + } + } + + // Forces the generation of a character entity for the specified Unicode character value. + public override void WriteCharEntity(char ch) { + try { + AutoComplete(Token.Content); + xmlEncoder.WriteCharEntity(ch); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the given whitespace. + public override void WriteWhitespace(string ws) { + try { + if (null == ws) { + ws = String.Empty; + } + + if (!xmlCharType.IsOnlyWhitespace(ws)) { + throw new ArgumentException("Xml_NonWhitespace"); + } + AutoComplete(Token.Whitespace); + xmlEncoder.Write(ws); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the specified text content. + public override void WriteString(string text) { + try { + if (null != text && text.Length != 0 ) { + AutoComplete(Token.Content); + xmlEncoder.Write(text); + } + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the specified surrogate pair as a character entity. + public override void WriteSurrogateCharEntity(char lowChar, char highChar){ + try { + AutoComplete(Token.Content); + xmlEncoder.WriteSurrogateCharEntity(lowChar, highChar); + } + catch { + currentState = State.Error; + throw; + } + } + + + // Writes out the specified text content. + public override void WriteChars(Char[] buffer, int index, int count) { + try { + AutoComplete(Token.Content); + xmlEncoder.Write(buffer, index, count); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes raw markup from the specified character buffer. + public override void WriteRaw(Char[] buffer, int index, int count) { + try { + AutoComplete(Token.RawData); + xmlEncoder.WriteRaw(buffer, index, count); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes raw markup from the specified character string. + public override void WriteRaw(String data) { + try { + AutoComplete(Token.RawData); + xmlEncoder.WriteRawWithSurrogateChecking(data); + } + catch { + currentState = State.Error; + throw; + } + } + + // Encodes the specified binary bytes as base64 and writes out the resulting text. + public override void WriteBase64(byte[] buffer, int index, int count) { + try { + if (!this.flush) { + AutoComplete(Token.Base64); + } + + this.flush = true; + // No need for us to explicitly validate the args. The StreamWriter will do + // it for us. + if (null == this.base64Encoder) { + this.base64Encoder = new XmlTextWriterBase64Encoder( xmlEncoder ); + } + // Encode will call WriteRaw to write out the encoded characters + this.base64Encoder.Encode( buffer, index, count ); + } + catch { + currentState = State.Error; + throw; + } + } + + + // Encodes the specified binary bytes as binhex and writes out the resulting text. + public override void WriteBinHex( byte[] buffer, int index, int count ) { + try { + AutoComplete( Token.Content ); + BinHexEncoder.Encode( buffer, index, count, this ); + } + catch { + currentState = State.Error; + throw; + } + } + + // Returns the state of the XmlWriter. + public override WriteState WriteState { + get { + switch (this.currentState) { + case State.Start : + return WriteState.Start; + case State.Prolog : + case State.PostDTD : + return WriteState.Prolog; + case State.Element : + return WriteState.Element; + case State.Attribute : + case State.AttrOnly: + return WriteState.Attribute; + case State.Content : + case State.Epilog : + return WriteState.Content; + case State.Error: + return WriteState.Error; + case State.Closed: + return WriteState.Closed; + default: + Debug.Assert( false ); + return WriteState.Error; + } + } + } + + // Closes the XmlWriter and the underlying stream/TextWriter. + protected override void Dispose(bool disposing) + { + if (disposing) + { + try + { + AutoCompleteAll(); + } + catch + { // never fail + } + finally + { + this.currentState = State.Closed; + textWriter.Dispose(); + } + } + + base.Dispose(disposing); + } + + // Flushes whatever is in the buffer to the underlying stream/TextWriter and flushes the underlying stream/TextWriter. + public override void Flush() { + textWriter.Flush(); + } + + // Writes out the specified name, ensuring it is a valid Name according to the XML specification + // (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name + public override void WriteName(string name) { + try { + AutoComplete(Token.Content); + InternalWriteName(name, false); + } + catch { + currentState = State.Error; + throw; + } + } + + // Writes out the specified namespace-qualified name by looking up the prefix that is in scope for the given namespace. + public override void WriteQualifiedName(string localName, string ns) { + try { + AutoComplete(Token.Content); + if (this.namespaces) { + if (ns != null && ns.Length != 0 && ns != stack[top].defaultNs) { + string prefix = FindPrefix(ns); + if (prefix == null) { + if (this.currentState != State.Attribute) { + throw new ArgumentException("Xml_UndefNamespace: " + ns); + } + prefix = GeneratePrefix(); // need a prefix if + PushNamespace(prefix, ns, false); + } + if (prefix.Length != 0) { + InternalWriteName(prefix, true); + textWriter.Write(':'); + } + } + } + else if (ns != null && ns.Length != 0) { + throw new ArgumentException("Xml_NoNamespaces"); + } + InternalWriteName(localName, true); + } + catch { + currentState = State.Error; + throw; + } + } + + // Returns the closest prefix defined in the current namespace scope for the specified namespace URI. + public override string LookupPrefix(string ns) { + if (ns == null || ns.Length == 0) { + throw new ArgumentException("Xml_EmptyName"); + } + string s = FindPrefix(ns); + if (s == null && ns == stack[top].defaultNs) { + s = string.Empty; + } + return s; + } + + // Gets an XmlSpace representing the current xml:space scope. + public override XmlSpace XmlSpace { + get { + for (int i = top; i > 0; i--) { + XmlSpace xs = stack[i].xmlSpace; + if (xs != XmlSpace.None) + return xs; + } + return XmlSpace.None; + } + } + + // Gets the current xml:lang scope. + public override string XmlLang { + get { + for (int i = top; i > 0; i--) { + String xlang = stack[i].xmlLang; + if (xlang != null) + return xlang; + } + return null; + } + } + + // Writes out the specified name, ensuring it is a valid NmToken + // according to the XML specification (http://www.w3.org/TR/1998/REC-xml-19980210#NT-Name). + public override void WriteNmToken(string name) { + try { + AutoComplete(Token.Content); + + if (name == null || name.Length == 0) { + throw new ArgumentException("Xml_EmptyName"); + } + if (!ValidateNames.IsNmtokenNoNamespaces(name)) { + throw new ArgumentException("Xml_InvalidNameChars: " + name); + } + textWriter.Write(name); + } + catch { + currentState = State.Error; + throw; + } + } + +// +// Private implementation methods +// + void StartDocument(int standalone) { + try { + if (this.currentState != State.Start) { + throw new InvalidOperationException("Xml_NotTheFirst"); + } + this.stateTable = stateTableDocument; + this.currentState = State.Prolog; + + StringBuilder bufBld = new StringBuilder(128); + bufBld.Append("version=" + quoteChar + "1.0" + quoteChar); + if (this.encoding != null) { + bufBld.Append(" encoding="); + bufBld.Append(quoteChar); + bufBld.Append(this.encoding.WebName); + bufBld.Append(quoteChar); + } + if (standalone >= 0) { + bufBld.Append(" standalone="); + bufBld.Append(quoteChar); + bufBld.Append(standalone == 0 ? "no" : "yes"); + bufBld.Append(quoteChar); + } + InternalWriteProcessingInstruction("xml", bufBld.ToString()); + } + catch { + currentState = State.Error; + throw; + } + } + + void AutoComplete(Token token) { + if (this.currentState == State.Closed) { + throw new InvalidOperationException("Xml_Closed"); + } + else if (this.currentState == State.Error) { + throw new InvalidOperationException("Xml_WrongToken: " + tokenName[(int)token] + ", " + stateName[(int)State.Error]); + } + + State newState = this.stateTable[(int)token * 8 + (int)this.currentState]; + if (newState == State.Error) { + throw new InvalidOperationException("Xml_WrongToken: " + tokenName[(int)token] + ", " + stateName[(int)this.currentState]); + } + + switch (token) { + case Token.Doctype: + if (this.indented && this.currentState != State.Start) { + Indent(false); + } + break; + + case Token.StartElement: + case Token.Comment: + case Token.PI: + case Token.CData: + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + WriteEndStartTag(false); + } + else if (this.currentState == State.Element) { + WriteEndStartTag(false); + } + if (token == Token.CData) { + stack[top].mixed = true; + } + else if (this.indented && this.currentState != State.Start) { + Indent(false); + } + break; + + case Token.EndElement: + case Token.LongEndElement: + if (this.flush) { + FlushEncoders(); + } + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + } + if (this.currentState == State.Content) { + token = Token.LongEndElement; + } + else { + WriteEndStartTag(token == Token.EndElement); + } + if (stateTableDocument == this.stateTable && top == 1) { + newState = State.Epilog; + } + break; + + case Token.StartAttribute: + if (this.flush) { + FlushEncoders(); + } + if (this.currentState == State.Attribute) { + WriteEndAttributeQuote(); + textWriter.Write(' '); + } + else if (this.currentState == State.Element) { + textWriter.Write(' '); + } + break; + + case Token.EndAttribute: + if (this.flush) { + FlushEncoders(); + } + WriteEndAttributeQuote(); + break; + + case Token.Whitespace: + case Token.Content: + case Token.RawData: + case Token.Base64: + + if (token != Token.Base64 && this.flush) { + FlushEncoders(); + } + if (this.currentState == State.Element && this.lastToken != Token.Content) { + WriteEndStartTag(false); + } + if (newState == State.Content) { + stack[top].mixed = true; + } + break; + + default: + throw new InvalidOperationException("Xml_InvalidOperation"); + } + this.currentState = newState; + this.lastToken = token; + } + + void AutoCompleteAll() { + if (this.flush) { + FlushEncoders(); + } + while (top > 0) { + WriteEndElement(); + } + } + + void InternalWriteEndElement(bool longFormat) { + try { + if (top <= 0) { + throw new InvalidOperationException("Xml_NoStartTag"); + } + // if we are in the element, we need to close it. + AutoComplete(longFormat ? Token.LongEndElement : Token.EndElement); + if (this.lastToken == Token.LongEndElement) { + if (this.indented) { + Indent(true); + } + textWriter.Write('<'); + textWriter.Write('/'); + if (this.namespaces && stack[top].prefix != null) { + textWriter.Write(stack[top].prefix); + textWriter.Write(':'); + } + textWriter.Write(stack[top].name); + textWriter.Write('>'); + } + + // pop namespaces + int prevNsTop = stack[top].prevNsTop; + if (useNsHashtable && prevNsTop < nsTop) { + PopNamespaces(prevNsTop + 1, nsTop); + } + nsTop = prevNsTop; + top--; + } + catch { + currentState = State.Error; + throw; + } + } + + void WriteEndStartTag(bool empty) { + xmlEncoder.StartAttribute(false); + for (int i = nsTop; i > stack[top].prevNsTop; i--) { + if (!nsStack[i].declared) { + textWriter.Write(" xmlns"); + textWriter.Write(':'); + textWriter.Write(nsStack[i].prefix); + textWriter.Write('='); + textWriter.Write(this.quoteChar); + xmlEncoder.Write(nsStack[i].ns); + textWriter.Write(this.quoteChar); + } + } + // Default + if ((stack[top].defaultNs != stack[top - 1].defaultNs) && + (stack[top].defaultNsState == NamespaceState.DeclaredButNotWrittenOut)) { + textWriter.Write(" xmlns"); + textWriter.Write('='); + textWriter.Write(this.quoteChar); + xmlEncoder.Write(stack[top].defaultNs); + textWriter.Write(this.quoteChar); + stack[top].defaultNsState = NamespaceState.DeclaredAndWrittenOut; + } + xmlEncoder.EndAttribute(); + if (empty) { + textWriter.Write(" /"); + } + textWriter.Write('>'); + } + + void WriteEndAttributeQuote() { + if (this.specialAttr != SpecialAttr.None) { + // Ok, now to handle xmlspace, etc. + HandleSpecialAttribute(); + } + xmlEncoder.EndAttribute(); + textWriter.Write(this.curQuoteChar); + } + + void Indent(bool beforeEndElement) { + // pretty printing. + if (top == 0) { + textWriter.WriteLine(); + } + else if (!stack[top].mixed) { + textWriter.WriteLine(); + int i = beforeEndElement ? top - 1 : top; + for (i *= this.indentation; i > 0; i--) { + textWriter.Write(this.indentChar); + } + } + } + + // pushes new namespace scope, and returns generated prefix, if one + // was needed to resolve conflicts. + void PushNamespace(string prefix, string ns, bool declared) { + if (XmlReservedNs.NsXmlNs == ns) { + throw new ArgumentException("Xml_CanNotBindToReservedNamespace"); + } + + if (prefix == null) { + switch (stack[top].defaultNsState) { + case NamespaceState.DeclaredButNotWrittenOut: + Debug.Assert (declared == true, "Unexpected situation!!"); + // the first namespace that the user gave us is what we + // like to keep. + break; + case NamespaceState.Uninitialized: + case NamespaceState.NotDeclaredButInScope: + // we now got a brand new namespace that we need to remember + stack[top].defaultNs = ns; + break; + default: + Debug.Assert(false, "Should have never come here"); + return; + } + stack[top].defaultNsState = (declared ? NamespaceState.DeclaredAndWrittenOut : NamespaceState.DeclaredButNotWrittenOut); + } + else { + if (prefix.Length != 0 && ns.Length == 0) { + throw new ArgumentException("Xml_PrefixForEmptyNs"); + } + + int existingNsIndex = LookupNamespace(prefix); + if (existingNsIndex != -1 && nsStack[existingNsIndex].ns == ns) { + // it is already in scope. + if (declared) { + nsStack[existingNsIndex].declared = true; + } + } + else { + // see if prefix conflicts for the current element + if (declared) { + if (existingNsIndex != -1 && existingNsIndex > stack[top].prevNsTop) { + nsStack[existingNsIndex].declared = true; // old one is silenced now + } + } + AddNamespace(prefix, ns, declared); + } + } + } + + void AddNamespace(string prefix, string ns, bool declared) { + int nsIndex = ++nsTop; + if ( nsIndex == nsStack.Length ) { + Namespace[] newStack = new Namespace[nsIndex * 2]; + Array.Copy(nsStack, newStack, nsIndex); + nsStack = newStack; + } + nsStack[nsIndex].Set(prefix, ns, declared); + + if (useNsHashtable) { + AddToNamespaceHashtable(nsIndex); + } + else if (nsIndex == MaxNamespacesWalkCount) { + // add all + nsHashtable = new Dictionary(new SecureStringHasher()); + for (int i = 0; i <= nsIndex; i++) { + AddToNamespaceHashtable(i); + } + useNsHashtable = true; + } + } + + void AddToNamespaceHashtable(int namespaceIndex) { + string prefix = nsStack[namespaceIndex].prefix; + int existingNsIndex; + if ( nsHashtable.TryGetValue(prefix, out existingNsIndex)) { + nsStack[namespaceIndex].prevNsIndex = existingNsIndex; + } + nsHashtable[prefix] = namespaceIndex; + } + + private void PopNamespaces(int indexFrom, int indexTo) { + Debug.Assert(useNsHashtable); + for (int i = indexTo; i >= indexFrom; i--) { + Debug.Assert(nsHashtable.ContainsKey(nsStack[i].prefix)); + if (nsStack[i].prevNsIndex == -1) { + nsHashtable.Remove(nsStack[i].prefix); + } + else { + nsHashtable[nsStack[i].prefix] = nsStack[i].prevNsIndex; + } + } + } + + string GeneratePrefix() { + int temp = stack[top].prefixCount++ + 1; + return "d" + top.ToString("d", CultureInfo.InvariantCulture) + + "p" + temp.ToString("d", CultureInfo.InvariantCulture); + } + + void InternalWriteProcessingInstruction(string name, string text) { + textWriter.Write(""); + } + + int LookupNamespace( string prefix ) { + if ( useNsHashtable ) { + int nsIndex; + if ( nsHashtable.TryGetValue( prefix, out nsIndex ) ) { + return nsIndex; + } + } + else { + for ( int i = nsTop; i >= 0; i-- ) { + if ( nsStack[i].prefix == prefix ) { + return i; + } + } + } + return -1; + } + + int LookupNamespaceInCurrentScope( string prefix ) { + if ( useNsHashtable ) { + int nsIndex; + if ( nsHashtable.TryGetValue( prefix, out nsIndex ) ) { + if ( nsIndex > stack[top].prevNsTop ) { + return nsIndex; + } + } + } + else { + for ( int i = nsTop; i > stack[top].prevNsTop; i-- ) { + if ( nsStack[i].prefix == prefix ) { + return i; + } + } + } + return -1; + } + + string FindPrefix(string ns) { + for (int i = nsTop; i >= 0; i--) { + if (nsStack[i].ns == ns) { + if (LookupNamespace(nsStack[i].prefix) == i) { + return nsStack[i].prefix; + } + } + } + return null; + } + + // There are three kind of strings we write out - Name, LocalName and Prefix. + // Both LocalName and Prefix can be represented with NCName == false and Name + // can be represented as NCName == true + + void InternalWriteName(string name, bool isNCName) { + ValidateName(name, isNCName); + textWriter.Write(name); + } + + // This method is used for validation of the DOCTYPE, processing instruction and entity names plus names + // written out by the user via WriteName and WriteQualifiedName. + // Unfortunatelly the names of elements and attributes are not validated by the XmlTextWriter. + // Also this method does not check wheather the character after ':' is a valid start name character. It accepts + // all valid name characters at that position. This can't be changed because of backwards compatibility. + private unsafe void ValidateName(string name, bool isNCName) { + if (name == null || name.Length == 0) { + throw new ArgumentException("Xml_EmptyName"); + } + + int nameLength = name.Length; + + // Namespaces supported + if (namespaces) { + // We can't use ValidateNames.ParseQName here because of backwards compatibility bug we need to preserve. + // The bug is that the character after ':' is validated only as a NCName characters instead of NCStartName. + int colonPosition = -1; + + // Parse NCName (may be prefix, may be local name) + int position = ValidateNames.ParseNCName(name); + + Continue: + if (position == nameLength) { + return; + } + + // we have prefix:localName + if (name[position] == ':') { + if (!isNCName) { + // first colon in qname + if (colonPosition == -1) { + // make sure it is not the first or last characters + if (position > 0 && position + 1 < nameLength) { + colonPosition = position; + // Because of the back-compat bug (described above) parse the rest as Nmtoken + position++; + position += ValidateNames.ParseNmtoken(name, position); + goto Continue; + } + } + } + } + } + // Namespaces not supported + else { + if (ValidateNames.IsNameNoNamespaces(name)) { + return; + } + } + throw new ArgumentException("Xml_InvalidNameChars: " + name); + } + + // XML whitespace characters, http://www.w3.org/TR/REC-xml#NT-S + internal static readonly char[] WhitespaceChars = new char[] { ' ', '\t', '\n', '\r' }; + + // Trim a string using XML whitespace characters + internal static string TrimString(string value) { + return value.Trim(WhitespaceChars); + } + + void HandleSpecialAttribute() { + string value = xmlEncoder.AttributeValue; + switch (this.specialAttr) { + case SpecialAttr.XmlLang: + stack[top].xmlLang = value; + break; + case SpecialAttr.XmlSpace: + // validate XmlSpace attribute + value = TrimString(value); + if (value == "default") { + stack[top].xmlSpace = XmlSpace.Default; + } + else if (value == "preserve") { + stack[top].xmlSpace = XmlSpace.Preserve; + } + else { + throw new ArgumentException("Xml_InvalidXmlSpace: " + value); + } + break; + case SpecialAttr.XmlNs: + VerifyPrefixXml(this.prefixForXmlNs, value); + PushNamespace(this.prefixForXmlNs, value, true); + break; + } + } + + + void VerifyPrefixXml(string prefix, string ns) { + + if (prefix != null && prefix.Length == 3) { + if ( + (prefix[0] == 'x' || prefix[0] == 'X') && + (prefix[1] == 'm' || prefix[1] == 'M') && + (prefix[2] == 'l' || prefix[2] == 'L') + ) { + if (XmlReservedNs.NsXml != ns) { + throw new ArgumentException("Xml_InvalidPrefix"); + } + } + } + } + + void PushStack() { + if (top == stack.Length - 1) { + TagInfo[] na = new TagInfo[stack.Length + 10]; + if (top > 0) Array.Copy(stack,na,top + 1); + stack = na; + } + + top++; // Move up stack + stack[top].Init(nsTop); + } + + void FlushEncoders() + { + if (null != this.base64Encoder) { + // The Flush will call WriteRaw to write out the rest of the encoded characters + this.base64Encoder.Flush(); + } + this.flush = false; + } + } +} \ No newline at end of file diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodePacketTranslatorExtensions.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodePacketTranslatorExtensions.cs index a3f20362f7f..f3e3271bef1 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodePacketTranslatorExtensions.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodePacketTranslatorExtensions.cs @@ -13,7 +13,9 @@ using System.Text; using System.IO; using System.Threading; +#if FEATURE_BINARY_SERIALIZATION using System.Runtime.Serialization.Formatters.Binary; +#endif using Microsoft.Build.Collections; using Microsoft.Build.Execution; using Microsoft.Build.Framework; diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderInProc.cs index 98b7ca8c214..b00b4cbf7d3 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -212,11 +212,15 @@ public bool CreateNode(int nodeId, INodePacketFactory factory, NodeConfiguration var p = Process.GetCurrentProcess(); { // This should be reasonably sufficient to assume MS Expression Blend 4 or earlier - if ((FileUtilities.CurrentExecutableName.Equals("Blend", StringComparison.OrdinalIgnoreCase)) && - (p.MainModule.FileVersionInfo.OriginalFilename.Equals("Blend.exe", StringComparison.OrdinalIgnoreCase)) && - (p.MainModule.FileVersionInfo.ProductMajorPart < 5)) + if ((FileUtilities.CurrentExecutableName.Equals("Blend", StringComparison.OrdinalIgnoreCase))) { - _exclusiveOperatingEnvironment = false; + FileVersionInfo mainModuleFileVersionInfo = FileVersionInfo.GetVersionInfo(p.MainModule.FileName); + if (mainModuleFileVersionInfo.OriginalFilename.Equals("Blend.exe", StringComparison.OrdinalIgnoreCase) && + (mainModuleFileVersionInfo.ProductMajorPart < 5)) + { + _exclusiveOperatingEnvironment = false; + } + } } diff --git a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 5a4114887c3..0e4fb04975d 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -145,7 +145,7 @@ protected void ShutdownAllNodes(long hostHandshake, long clientHandshake, NodeCo CommunicationsUtilities.Trace("Shutting down node with pid = {0}", nodeProcess.Id); NodeContext nodeContext = new NodeContext(0, nodeProcess.Id, nodeStream, factory, terminateNode); nodeContext.SendData(new NodeBuildComplete(false /* no node reuse */)); - nodeStream.Close(); + nodeStream.Dispose(); } } } @@ -350,7 +350,7 @@ private NamedPipeClientStream TryConnectToProcess(int nodeProcessId, int timeout // If we don't close any stream, we might hang up the child if (nodeStream != null) { - nodeStream.Close(); + nodeStream.Dispose(); } } diff --git a/src/XMakeBuildEngine/Logging/ParallelLogger/ParallelConsoleLogger.cs b/src/XMakeBuildEngine/Logging/ParallelLogger/ParallelConsoleLogger.cs index 8f24bfa623f..8e2ebff479b 100644 --- a/src/XMakeBuildEngine/Logging/ParallelLogger/ParallelConsoleLogger.cs +++ b/src/XMakeBuildEngine/Logging/ParallelLogger/ParallelConsoleLogger.cs @@ -80,6 +80,7 @@ private void CheckIfOutputSupportsAlignment() // If forceNoAlign is set there is no point getting the console width as there will be no aligning of the text if (!_forceNoAlign) { +#if FEATURE_CONSOLE_BUFFERWIDTH if (runningWithCharacterFileType) { // Get the size of the console buffer so messages can be formatted to the console width @@ -96,6 +97,7 @@ private void CheckIfOutputSupportsAlignment() } } else +#endif { _alignMessages = false; } diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index 4a1a8f87412..b194069a1a4 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -1,9 +1,9 @@  - - + Debug AnyCPU @@ -24,13 +24,46 @@ - - + + + + + SharedUtilities\Compat\Base64Encoder.cs + + + SharedUtilities\Compat\BinHexEncoder.cs + + SharedUtilities\Compat\SafeHandleZeroOrMinusOneIsInvalid.cs - + + SharedUtilities\Compat\SecureStringHasher.cs + + SharedUtilities\Compat\TypeFilter.cs + + SharedUtilities\Compat\ValidateNames.cs + + + SharedUtilities\Compat\XmlCharType.cs + + + SharedUtilities\Compat\XmlReservedNs.cs + + + SharedUtilities\Compat\XmlTextEncoder.cs + + + SharedUtilities\Compat\XmlTextWriter.cs + + + SharedUtilities\Compat\SerializableAttribute.cs + + + SharedUtilities\FileUtilities.GetFolderPath.cs @@ -637,11 +670,6 @@ true - - - SharedUtilities\Compat\SerializableAttribute.cs - - @@ -672,8 +700,8 @@ - - + \ No newline at end of file diff --git a/src/XMakeBuildEngine/project.json b/src/XMakeBuildEngine/project.json index 053615af849..fa66c16dd18 100644 --- a/src/XMakeBuildEngine/project.json +++ b/src/XMakeBuildEngine/project.json @@ -30,6 +30,7 @@ "System.Runtime.InteropServices.RuntimeInformation": "4.0.0-beta-23213", "System.Threading.Thread": "4.0.0-beta-23123", "System.Threading.ThreadPool": "4.0.10-beta-23123", + "System.Xml.XmlDocument": "4.0.0", "Microsoft.NETCore.Runtime": "1.0.0", "Microsoft.NETCore.TestHost-x86": "1.0.0-beta-23123" } diff --git a/src/XMakeBuildEngine/project.lock.json b/src/XMakeBuildEngine/project.lock.json index 2b0cbd0fcca..23d9cc2daed 100644 --- a/src/XMakeBuildEngine/project.lock.json +++ b/src/XMakeBuildEngine/project.lock.json @@ -1306,6 +1306,26 @@ "runtime": { "lib/dotnet/System.Xml.XDocument.dll": {} } + }, + "System.Xml.XmlDocument/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.IO": "[4.0.10, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Text.Encoding": "[4.0.10, )", + "System.Collections": "[4.0.10, )", + "System.Diagnostics.Debug": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Threading": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XmlDocument.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XmlDocument.dll": {} + } } }, ".NETFramework,Version=v4.5.1/win7-x86": { @@ -2819,6 +2839,26 @@ "runtime": { "lib/dotnet/System.Xml.XDocument.dll": {} } + }, + "System.Xml.XmlDocument/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.IO": "[4.0.10, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Text.Encoding": "[4.0.10, )", + "System.Collections": "[4.0.10, )", + "System.Diagnostics.Debug": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Threading": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XmlDocument.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XmlDocument.dll": {} + } } } }, @@ -5682,6 +5722,38 @@ "package/services/metadata/core-properties/f5c45d6b065347dfaa1d90d06221623d.psmdcp", "[Content_Types].xml" ] + }, + "System.Xml.XmlDocument/4.0.0": { + "sha512": "H5qTx2+AXgaKE5wehU1ZYeYPFpp/rfFh69/937NvwCrDqbIkvJRmIFyKKpkoMI6gl9hGfuVizfIudVTMyowCXw==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Xml.XmlDocument.nuspec", + "lib/dotnet/System.Xml.XmlDocument.dll", + "lib/net46/System.Xml.XmlDocument.dll", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "ref/dotnet/System.Xml.XmlDocument.dll", + "ref/dotnet/System.Xml.XmlDocument.xml", + "ref/dotnet/zh-hant/System.Xml.XmlDocument.xml", + "ref/dotnet/de/System.Xml.XmlDocument.xml", + "ref/dotnet/fr/System.Xml.XmlDocument.xml", + "ref/dotnet/it/System.Xml.XmlDocument.xml", + "ref/dotnet/ja/System.Xml.XmlDocument.xml", + "ref/dotnet/ko/System.Xml.XmlDocument.xml", + "ref/dotnet/ru/System.Xml.XmlDocument.xml", + "ref/dotnet/zh-hans/System.Xml.XmlDocument.xml", + "ref/dotnet/es/System.Xml.XmlDocument.xml", + "ref/net46/System.Xml.XmlDocument.dll", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "package/services/metadata/core-properties/89840371bf3f4e0d9ab7b6b34213c74c.psmdcp", + "[Content_Types].xml" + ] } }, "projectFileDependencyGroups": { @@ -5705,6 +5777,7 @@ "System.Runtime.InteropServices.RuntimeInformation >= 4.0.0-beta-23213", "System.Threading.Thread >= 4.0.0-beta-23123", "System.Threading.ThreadPool >= 4.0.10-beta-23123", + "System.Xml.XmlDocument >= 4.0.0", "Microsoft.NETCore.Runtime >= 1.0.0", "Microsoft.NETCore.TestHost-x86 >= 1.0.0-beta-23123" ] From f0e9a68b7d55d45141b776582577a2337ee7aced Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Fri, 21 Aug 2015 11:48:42 -0700 Subject: [PATCH 9/9] Port various code in Microsoft.Build to .NET Core --- dir.props | 3 + src/Shared/CommunicationsUtilities.cs | 2 +- src/Shared/Compat/TypeExtensions.cs | 12 ++ src/Shared/ExceptionHandling.cs | 4 +- src/Shared/FileUtilities.cs | 16 +- src/Shared/RegisteredTaskObjectCacheBase.cs | 2 + src/Shared/TaskLoggingHelper.cs | 4 +- src/Shared/TypeLoader.cs | 9 ++ src/Shared/XmlUtilities.cs | 5 +- .../BuildRequestEngine/BuildRequestEngine.cs | 2 +- .../RequestBuilder/RequestBuilder.cs | 6 +- .../BackEnd/Components/Scheduler/Scheduler.cs | 8 +- .../BackEnd/Node/OutOfProcNode.cs | 4 + .../TaskExecutionHost/TaskExecutionHost.cs | 4 + .../Construction/ProjectElement.cs | 1 + .../Construction/ProjectElementContainer.cs | 1 + .../Construction/ProjectRootElement.cs | 11 ++ .../XmlDocumentWithLocation.cs | 4 + .../Evaluation/ProjectRootElementCache.cs | 8 +- .../Instance/ProjectItemInstance.cs | 2 +- .../Instance/ReflectableTaskPropertyInfo.cs | 6 +- src/XMakeBuildEngine/Logging/FileLogger.cs | 2 +- src/XMakeBuildEngine/Microsoft.Build.csproj | 6 +- src/XMakeBuildEngine/project.json | 1 + src/XMakeBuildEngine/project.lock.json | 141 ++++++++++++++++++ 25 files changed, 239 insertions(+), 25 deletions(-) create mode 100644 src/Shared/Compat/TypeExtensions.cs diff --git a/dir.props b/dir.props index 9f55e473afe..3921a44c476 100644 --- a/dir.props +++ b/dir.props @@ -113,6 +113,7 @@ $(DefineConstants);FEATURE_CONSTRAINED_EXECUTION $(DefineConstants);FEATURE_CHARSET_AUTO $(DefineConstants);FEATURE_DOTNETVERSION + $(DefineConstants);FEATURE_ENVIRONMENT_SYSTEMDIRECTORY $(DefineConstants);FEATURE_FILE_TRACKER true $(DefineConstants);FEATURE_GAC @@ -137,6 +138,8 @@ $(DefineConstants);FEATURE_XAML_TYPES true $(DefineConstants);FEATURE_XML_SOURCE_URI + $(DefineConstants);FEATURE_XML_LOADPATH + $(DefineConstants);FEATURE_XMLTEXTREADER diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 2487415959f..b1fb76c85e9 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -583,7 +583,7 @@ internal static void Trace(int nodeId, string format, params object[] args) fileName += ".txt"; - using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId), append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(s_debugDumpPath, fileName), Process.GetCurrentProcess().Id, nodeId), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, args); long now = DateTime.UtcNow.Ticks; diff --git a/src/Shared/Compat/TypeExtensions.cs b/src/Shared/Compat/TypeExtensions.cs new file mode 100644 index 00000000000..af193f11e06 --- /dev/null +++ b/src/Shared/Compat/TypeExtensions.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace System.Reflection +{ + public static class TypeExtensions + { + public static bool IsEquivalentTo(this Type type, Type other) + { + return type.Equals(other); + } + } +} \ No newline at end of file diff --git a/src/Shared/ExceptionHandling.cs b/src/Shared/ExceptionHandling.cs index 725d3e926dc..54dea978537 100644 --- a/src/Shared/ExceptionHandling.cs +++ b/src/Shared/ExceptionHandling.cs @@ -319,14 +319,14 @@ internal static void DumpExceptionToFile(Exception ex) s_dumpFileName = Path.Combine(tempPath, "MSBuild_" + guid.ToString() + ".failure.txt"); - using (StreamWriter writer = FileUtilities.OpenFile(s_dumpFileName, append: true)) + using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) { writer.WriteLine("UNHANDLED EXCEPTIONS FROM PROCESS {0}:", Process.GetCurrentProcess().Id); writer.WriteLine("====================="); } } - using (StreamWriter writer = FileUtilities.OpenFile(s_dumpFileName, append: true)) + using (StreamWriter writer = FileUtilities.OpenWrite(s_dumpFileName, append: true)) { // "G" format is, e.g., 6/15/2008 9:15:07 PM writer.WriteLine(DateTime.Now.ToString("G", CultureInfo.CurrentCulture)); diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index 245887a619d..29249b75d23 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -1105,7 +1105,7 @@ private static string CurrentExecutableOverride } } - internal static StreamWriter OpenFile(string path, bool append, Encoding encoding = null) + internal static StreamWriter OpenWrite(string path, bool append, Encoding encoding = null) { const int DefaultFileStreamBufferSize = 4096; FileMode mode = append? FileMode.Append: FileMode.Create; @@ -1119,5 +1119,19 @@ internal static StreamWriter OpenFile(string path, bool append, Encoding encodin return new StreamWriter(fileStream, encoding); } } + + internal static StreamReader OpenRead(string path, Encoding encoding = null) + { + const int DefaultFileStreamBufferSize = 4096; + Stream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan); + if (encoding == null) + { + return new StreamReader(fileStream); + } + else + { + return new StreamReader(fileStream, encoding); + } + } } } diff --git a/src/Shared/RegisteredTaskObjectCacheBase.cs b/src/Shared/RegisteredTaskObjectCacheBase.cs index 31dc0e29251..8057b8e3c26 100644 --- a/src/Shared/RegisteredTaskObjectCacheBase.cs +++ b/src/Shared/RegisteredTaskObjectCacheBase.cs @@ -33,6 +33,7 @@ internal class RegisteredTaskObjectCacheBase /// private Lazy> _buildLifetimeObjects = new Lazy>(); +#if FEATURE_APPDOMAIN /// /// Static constructor which registers a callback to dispose of AppDomain-lifetime cache objects. /// @@ -43,6 +44,7 @@ static RegisteredTaskObjectCacheBase() DisposeObjects(s_appDomainLifetimeObjects); }); } +#endif #region IRegisteredTaskObjectCache diff --git a/src/Shared/TaskLoggingHelper.cs b/src/Shared/TaskLoggingHelper.cs index 43d412d4e5c..62ebdf75d55 100644 --- a/src/Shared/TaskLoggingHelper.cs +++ b/src/Shared/TaskLoggingHelper.cs @@ -106,7 +106,7 @@ private string TaskNameUpperCase if (_taskNameUpperCase == null) { // NOTE: use the current thread culture, because this string will be displayed to the user - _taskNameUpperCase = TaskName.ToUpper(CultureInfo.CurrentCulture); + _taskNameUpperCase = TaskName.ToUpper(); } return _taskNameUpperCase; @@ -1235,7 +1235,7 @@ public bool LogMessagesFromFile(string fileName, MessageImportance messageImport // Command-line tools are generally going to emit their output using the current // codepage, so that it displays correctly in the console window. - using (StreamReader fileStream = new StreamReader(fileName, System.Text.Encoding.GetEncoding(0))) // HIGHCHAR: Use ANSI for logging messages. + using (StreamReader fileStream = FileUtilities.OpenRead(fileName, System.Text.Encoding.GetEncoding(0))) // HIGHCHAR: Use ANSI for logging messages. { errorsFound = LogMessagesFromStream(fileStream, messageImportance); } diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 005cf489abd..06b353bac44 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -363,11 +363,20 @@ private void ScanAssemblyForPublicTypes() { if (_assemblyLoadInfo.AssemblyName != null) { +#if FEATURE_ASSEMBLY_LOADFROM _loadedAssembly = Assembly.Load(_assemblyLoadInfo.AssemblyName); +#else + _loadedAssembly = Assembly.Load(new AssemblyName(_assemblyLoadInfo.AssemblyName)); +#endif } else { +#if FEATURE_ASSEMBLY_LOADFROM _loadedAssembly = Assembly.UnsafeLoadFrom(_assemblyLoadInfo.AssemblyFile); +#else + string simpleName = Path.GetFileNameWithoutExtension(_assemblyLoadInfo.AssemblyFile); + _loadedAssembly = Assembly.Load(new AssemblyName(simpleName)); +#endif } } catch (ArgumentException e) diff --git a/src/Shared/XmlUtilities.cs b/src/Shared/XmlUtilities.cs index 106e99c1c06..e1d76af31c2 100644 --- a/src/Shared/XmlUtilities.cs +++ b/src/Shared/XmlUtilities.cs @@ -185,9 +185,10 @@ string attributeName try { - using (XmlTextReader xmlReader = new XmlTextReader(projectFileName)) + XmlReaderSettings xmlReaderSettings = new XmlReaderSettings(); + xmlReaderSettings.DtdProcessing = DtdProcessing.Ignore; + using (XmlReader xmlReader = XmlReader.Create(projectFileName, xmlReaderSettings)) { - xmlReader.DtdProcessing = DtdProcessing.Ignore; while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Element) diff --git a/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index b756a4a30d7..bd61f8ee040 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -1351,7 +1351,7 @@ private void TraceEngine(string format, params object[] stuff) { lock (this) { - using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, @"EngineTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, @"EngineTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, stuff); file.WriteLine("{0}({1})-{2}: {3}", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, DateTime.UtcNow.Ticks, message); diff --git a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/RequestBuilder.cs index 8dd59531ccc..b89c61965f0 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/RequestBuilder.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/RequestBuilder/RequestBuilder.cs @@ -216,8 +216,8 @@ public void BuildRequest(NodeLoggingContext loggingContext, BuildRequestEntry en public void ContinueRequest() { ErrorUtilities.VerifyThrow(HasActiveBuildRequest, "Request not building"); - ErrorUtilities.VerifyThrow(!_terminateEvent.WaitOne(0, false), "Request already terminated"); - ErrorUtilities.VerifyThrow(!_continueEvent.WaitOne(0, false), "Request already continued"); + ErrorUtilities.VerifyThrow(!_terminateEvent.WaitOne(0), "Request already terminated"); + ErrorUtilities.VerifyThrow(!_continueEvent.WaitOne(0), "Request already continued"); VerifyEntryInReadyState(); _continueResults = _requestEntry.Continue(); @@ -1132,8 +1132,10 @@ private void LoadProjectIntoConfiguration() } catch (DirectoryNotFoundException) { +#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY // Somehow the startup directory vanished. This can happen if build was started from a USB Key and it was removed. NativeMethodsShared.SetCurrentDirectory(Environment.SystemDirectory); +#endif } } diff --git a/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs b/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs index 6be1cdd6ee2..e7c3a631886 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Scheduler/Scheduler.cs @@ -2193,7 +2193,7 @@ private void TraceScheduler(string format, params object[] stuff) { if (_debugDumpState) { - StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true); + StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true); file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, _schedulingData.EventTime.Ticks); file.WriteLine(format, stuff); file.Flush(); @@ -2210,7 +2210,7 @@ private void DumpSchedulerState() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Scheduler state at timestamp {0}:", _schedulingData.EventTime.Ticks); file.WriteLine("------------------------------------------------", _schedulingData.EventTime.Ticks); @@ -2307,7 +2307,7 @@ private void DumpConfigurations() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Configurations used during this build"); file.WriteLine("-------------------------------------"); @@ -2341,7 +2341,7 @@ private void DumpRequests() { if (_schedulingData != null) { - using (StreamWriter file = FileUtilities.OpenFile(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) + using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerState_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { file.WriteLine("Requests used during the build:"); file.WriteLine("-------------------------------"); diff --git a/src/XMakeBuildEngine/BackEnd/Node/OutOfProcNode.cs b/src/XMakeBuildEngine/BackEnd/Node/OutOfProcNode.cs index 03b5f880056..b85ab644bdc 100644 --- a/src/XMakeBuildEngine/BackEnd/Node/OutOfProcNode.cs +++ b/src/XMakeBuildEngine/BackEnd/Node/OutOfProcNode.cs @@ -469,8 +469,10 @@ private NodeEngineShutdownReason HandleShutdown(out Exception exception) // Shutdown any Out Of Proc Nodes Created _taskHostNodeManager.ShutdownConnectedNodes(_shutdownReason == NodeEngineShutdownReason.BuildCompleteReuse); +#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY // Restore the original current directory. NativeMethodsShared.SetCurrentDirectory(Environment.SystemDirectory); +#endif // Restore the original environment. // If the node was never configured, this will be null. @@ -674,8 +676,10 @@ private void HandleNodeConfiguration(NodeConfiguration configuration) } catch (DirectoryNotFoundException) { +#if FEATURE_ENVIRONMENT_SYSTEMDIRECTORY // Somehow the startup directory vanished. This can happen if build was started from a USB Key and it was removed. NativeMethodsShared.SetCurrentDirectory(Environment.SystemDirectory); +#endif } // Replicate the environment. First, unset any environment variables set by the previous configuration. diff --git a/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs b/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs index 9c6e9201797..051fe2f0d50 100644 --- a/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs +++ b/src/XMakeBuildEngine/BackEnd/TaskExecutionHost/TaskExecutionHost.cs @@ -707,7 +707,11 @@ private void Cancel() // Let the task finish now. If cancellation worked, hopefully it finishes sooner than it would have otherwise. // If the task builder crashed, this could have already been disposed +#if FEATURE_HANDLE_SAFEWAITHANDLE if (!_taskExecutionIdle.SafeWaitHandle.IsClosed) +#else + if (!_taskExecutionIdle.GetSafeWaitHandle().IsClosed) +#endif { // Kick off a task to log the message so that we don't block the calling thread. Task.Run(async delegate diff --git a/src/XMakeBuildEngine/Construction/ProjectElement.cs b/src/XMakeBuildEngine/Construction/ProjectElement.cs index 2a96f31db9c..0025fd6acf1 100644 --- a/src/XMakeBuildEngine/Construction/ProjectElement.cs +++ b/src/XMakeBuildEngine/Construction/ProjectElement.cs @@ -17,6 +17,7 @@ using Microsoft.Build.Collections; using ProjectXmlUtilities = Microsoft.Build.Internal.ProjectXmlUtilities; +using System.Reflection; namespace Microsoft.Build.Construction { diff --git a/src/XMakeBuildEngine/Construction/ProjectElementContainer.cs b/src/XMakeBuildEngine/Construction/ProjectElementContainer.cs index 0ff7fa85041..da78e8dcc67 100644 --- a/src/XMakeBuildEngine/Construction/ProjectElementContainer.cs +++ b/src/XMakeBuildEngine/Construction/ProjectElementContainer.cs @@ -15,6 +15,7 @@ using System.Collections.ObjectModel; using Microsoft.Build.Construction; using Microsoft.Build.Collections; +using System.Reflection; namespace Microsoft.Build.Construction { diff --git a/src/XMakeBuildEngine/Construction/ProjectRootElement.cs b/src/XMakeBuildEngine/Construction/ProjectRootElement.cs index e525814a118..d5919b70fa7 100644 --- a/src/XMakeBuildEngine/Construction/ProjectRootElement.cs +++ b/src/XMakeBuildEngine/Construction/ProjectRootElement.cs @@ -1942,6 +1942,8 @@ private XmlDocumentWithLocation LoadDocument(string fullPath) string beginProjectLoad = String.Format(CultureInfo.CurrentCulture, "Load Project {0} From File - Start", fullPath); DataCollection.CommentMarkProfile(8806, beginProjectLoad); #endif + +#if FEATURE_XMLTEXTREADER using (XmlTextReader xtr = new XmlTextReader(fullPath)) { // Start the reader so it has an idea of what the encoding is. @@ -1950,6 +1952,15 @@ private XmlDocumentWithLocation LoadDocument(string fullPath) _encoding = xtr.Encoding; document.Load(xtr); } +#else + XmlReaderSettings xmlReaderSettings = new XmlReaderSettings(); + xmlReaderSettings.DtdProcessing = DtdProcessing.Ignore; + using (XmlReader xr = XmlReader.Create(File.OpenRead(fullPath), xmlReaderSettings)) + { + xr.Read(); + document.Load(xr); + } +#endif document.FullPath = fullPath; _projectFileLocation = ElementLocation.Create(fullPath); diff --git a/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs b/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs index b5b460c903c..e8a9ab453be 100644 --- a/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs +++ b/src/XMakeBuildEngine/ElementLocation/XmlDocumentWithLocation.cs @@ -160,6 +160,7 @@ public override void Load(XmlReader reader) _reader = null; } +#if FEATURE_XML_LOADPATH /// /// Grab the path to the file, for use in our location information. /// @@ -178,6 +179,7 @@ public override void Load(string fullPath) this.Load(xmlReader); } } +#endif /// /// Called during load, to add an element. @@ -278,6 +280,7 @@ public override void Save(Stream outStream) base.Save(outStream); } +#if FEATURE_XML_LOADPATH /// /// Override Save to verify file was not loaded as readonly /// @@ -286,6 +289,7 @@ public override void Save(string filename) VerifyThrowNotReadOnly(); base.Save(filename); } +#endif /// /// Override Save to verify file was not loaded as readonly diff --git a/src/XMakeBuildEngine/Evaluation/ProjectRootElementCache.cs b/src/XMakeBuildEngine/Evaluation/ProjectRootElementCache.cs index ae6e8b4ccaa..baa24d08b5f 100644 --- a/src/XMakeBuildEngine/Evaluation/ProjectRootElementCache.cs +++ b/src/XMakeBuildEngine/Evaluation/ProjectRootElementCache.cs @@ -227,10 +227,12 @@ internal ProjectRootElement Get(string projectFile, OpenProjectRootElement openP // use: it checks the file content as well as the timestamp. That's better than completely disabling // the cache as we get test coverage of the rest of the cache code. XmlDocument document = new XmlDocument(); - using (XmlTextReader xtr = new XmlTextReader(projectRootElement.FullPath)) + + XmlReaderSettings xmlReaderSettings = new XmlReaderSettings(); + xmlReaderSettings.DtdProcessing = DtdProcessing.Ignore; + using (XmlReader xr = XmlReader.Create(File.OpenRead(projectRootElement.FullPath), xmlReaderSettings)) { - xtr.DtdProcessing = DtdProcessing.Ignore; - document.Load(xtr); + document.Load(xr); } string diskContent = document.OuterXml; diff --git a/src/XMakeBuildEngine/Instance/ProjectItemInstance.cs b/src/XMakeBuildEngine/Instance/ProjectItemInstance.cs index 38a0a988f46..ac594c25de3 100644 --- a/src/XMakeBuildEngine/Instance/ProjectItemInstance.cs +++ b/src/XMakeBuildEngine/Instance/ProjectItemInstance.cs @@ -1460,7 +1460,7 @@ public override int GetHashCode() // We need to change this to upper case to ensure that task items whose item specs differ only by // casing still have the same hash code, since this is used to determine if we have duplicates when // we do duplicate removal. - return ItemSpec.ToUpper(CultureInfo.InvariantCulture).GetHashCode(); + return ItemSpec.ToUpperInvariant().GetHashCode(); } /// diff --git a/src/XMakeBuildEngine/Instance/ReflectableTaskPropertyInfo.cs b/src/XMakeBuildEngine/Instance/ReflectableTaskPropertyInfo.cs index eb165b94720..78d92f6eb55 100644 --- a/src/XMakeBuildEngine/Instance/ReflectableTaskPropertyInfo.cs +++ b/src/XMakeBuildEngine/Instance/ReflectableTaskPropertyInfo.cs @@ -50,8 +50,8 @@ internal ReflectableTaskPropertyInfo(PropertyInfo propertyInfo) : base( propertyInfo.Name, propertyInfo.PropertyType, - propertyInfo.GetCustomAttributes(typeof(OutputAttribute), true).Length > 0, - propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Length > 0) + propertyInfo.GetCustomAttributes(typeof(OutputAttribute), true).Any(), + propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Any()) { _propertyInfo = propertyInfo; } @@ -65,7 +65,7 @@ internal PropertyInfo Reflection { if (_propertyInfo == null) { - _propertyInfo = _taskType.GetProperty(Name, BindingFlags.ExactBinding | BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase); + _propertyInfo = _taskType.GetProperty(Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase); ErrorUtilities.VerifyThrow(_propertyInfo != null, "Could not find property {0} on type {1} that the task factory indicated should exist.", Name, _taskType.FullName); } diff --git a/src/XMakeBuildEngine/Logging/FileLogger.cs b/src/XMakeBuildEngine/Logging/FileLogger.cs index 8927b5ab467..b5115cc1ad9 100644 --- a/src/XMakeBuildEngine/Logging/FileLogger.cs +++ b/src/XMakeBuildEngine/Logging/FileLogger.cs @@ -80,7 +80,7 @@ private void InitializeFileLogger(IEventSource eventSource, int nodeCount) try { - _fileWriter = FileUtilities.OpenFile(_logFileName, _append, _encoding); + _fileWriter = FileUtilities.OpenWrite(_logFileName, _append, _encoding); _fileWriter.AutoFlush = _autoFlush; } diff --git a/src/XMakeBuildEngine/Microsoft.Build.csproj b/src/XMakeBuildEngine/Microsoft.Build.csproj index b194069a1a4..a2b2eeb1156 100644 --- a/src/XMakeBuildEngine/Microsoft.Build.csproj +++ b/src/XMakeBuildEngine/Microsoft.Build.csproj @@ -24,7 +24,6 @@ - @@ -41,9 +40,12 @@ SharedUtilities\Compat\SecureStringHasher.cs - + SharedUtilities\Compat\TypeFilter.cs + + SharedUtilities\Compat\TypeExtensions.cs + SharedUtilities\Compat\ValidateNames.cs diff --git a/src/XMakeBuildEngine/project.json b/src/XMakeBuildEngine/project.json index fa66c16dd18..ef0b143663c 100644 --- a/src/XMakeBuildEngine/project.json +++ b/src/XMakeBuildEngine/project.json @@ -31,6 +31,7 @@ "System.Threading.Thread": "4.0.0-beta-23123", "System.Threading.ThreadPool": "4.0.10-beta-23123", "System.Xml.XmlDocument": "4.0.0", + "System.Xml.XPath.XmlDocument": "4.0.0", "Microsoft.NETCore.Runtime": "1.0.0", "Microsoft.NETCore.TestHost-x86": "1.0.0-beta-23123" } diff --git a/src/XMakeBuildEngine/project.lock.json b/src/XMakeBuildEngine/project.lock.json index 23d9cc2daed..2f301f47757 100644 --- a/src/XMakeBuildEngine/project.lock.json +++ b/src/XMakeBuildEngine/project.lock.json @@ -1326,6 +1326,45 @@ "runtime": { "lib/dotnet/System.Xml.XmlDocument.dll": {} } + }, + "System.Xml.XPath/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.IO": "[4.0.10, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Collections": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Diagnostics.Debug": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Threading": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XPath.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XPath.dll": {} + } + }, + "System.Xml.XPath.XmlDocument/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Xml.XmlDocument": "[4.0.0, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.Xml.XPath": "[4.0.0, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Collections": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Threading": "[4.0.10, )", + "System.IO": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XPath.XmlDocument.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XPath.XmlDocument.dll": {} + } } }, ".NETFramework,Version=v4.5.1/win7-x86": { @@ -2859,6 +2898,45 @@ "runtime": { "lib/dotnet/System.Xml.XmlDocument.dll": {} } + }, + "System.Xml.XPath/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.IO": "[4.0.10, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Collections": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Diagnostics.Debug": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Threading": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XPath.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XPath.dll": {} + } + }, + "System.Xml.XPath.XmlDocument/4.0.0": { + "dependencies": { + "System.Runtime": "[4.0.20, )", + "System.Xml.XmlDocument": "[4.0.0, )", + "System.Xml.ReaderWriter": "[4.0.10, )", + "System.Xml.XPath": "[4.0.0, )", + "System.Resources.ResourceManager": "[4.0.0, )", + "System.Collections": "[4.0.10, )", + "System.Runtime.Extensions": "[4.0.10, )", + "System.Globalization": "[4.0.10, )", + "System.Threading": "[4.0.10, )", + "System.IO": "[4.0.10, )" + }, + "compile": { + "ref/dotnet/System.Xml.XPath.XmlDocument.dll": {} + }, + "runtime": { + "lib/dotnet/System.Xml.XPath.XmlDocument.dll": {} + } } } }, @@ -5754,6 +5832,68 @@ "package/services/metadata/core-properties/89840371bf3f4e0d9ab7b6b34213c74c.psmdcp", "[Content_Types].xml" ] + }, + "System.Xml.XPath/4.0.0": { + "sha512": "jalVwhZSwErcW28NZOE3Dqb6B1XA4DAsL15JvykYIKXtXYQU8PI5GXssjF5G0bLm8/6Gko2e1SOjRs/MoeAKrw==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Xml.XPath.nuspec", + "lib/dotnet/System.Xml.XPath.dll", + "lib/net46/System.Xml.XPath.dll", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "ref/dotnet/System.Xml.XPath.dll", + "ref/dotnet/System.Xml.XPath.xml", + "ref/dotnet/zh-hant/System.Xml.XPath.xml", + "ref/dotnet/de/System.Xml.XPath.xml", + "ref/dotnet/fr/System.Xml.XPath.xml", + "ref/dotnet/it/System.Xml.XPath.xml", + "ref/dotnet/ja/System.Xml.XPath.xml", + "ref/dotnet/ko/System.Xml.XPath.xml", + "ref/dotnet/ru/System.Xml.XPath.xml", + "ref/dotnet/zh-hans/System.Xml.XPath.xml", + "ref/dotnet/es/System.Xml.XPath.xml", + "ref/net46/System.Xml.XPath.dll", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "package/services/metadata/core-properties/d021107d37ac4c0c92e4a52a049b2db5.psmdcp", + "[Content_Types].xml" + ] + }, + "System.Xml.XPath.XmlDocument/4.0.0": { + "sha512": "toGFsezsdAJ3Fkau0l386iGBg3qfYauQrM4haX1nWPYnUOWb0hXfAEVIi47/Ke4ET3gl9h9ZppKiws8QWeJVyQ==", + "type": "Package", + "files": [ + "_rels/.rels", + "System.Xml.XPath.XmlDocument.nuspec", + "lib/dotnet/System.Xml.XPath.XmlDocument.dll", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "ref/dotnet/System.Xml.XPath.XmlDocument.dll", + "ref/dotnet/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/zh-hant/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/de/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/fr/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/it/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/ja/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/ko/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/ru/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/zh-hans/System.Xml.XPath.XmlDocument.xml", + "ref/dotnet/es/System.Xml.XPath.XmlDocument.xml", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "package/services/metadata/core-properties/d80c68d7cec54f53bc0a59c937fdbf0b.psmdcp", + "[Content_Types].xml" + ] } }, "projectFileDependencyGroups": { @@ -5778,6 +5918,7 @@ "System.Threading.Thread >= 4.0.0-beta-23123", "System.Threading.ThreadPool >= 4.0.10-beta-23123", "System.Xml.XmlDocument >= 4.0.0", + "System.Xml.XPath.XmlDocument >= 4.0.0", "Microsoft.NETCore.Runtime >= 1.0.0", "Microsoft.NETCore.TestHost-x86 >= 1.0.0-beta-23123" ]