Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Serialization / Compiler.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="Compiler.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>                                                                
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Serialization {
9     using System.Reflection;
10     using System.Reflection.Emit;
11     using System.Collections;
12     using System.IO;
13     using System;
14     using System.Text;
15     using System.ComponentModel;
16     using System.CodeDom.Compiler;
17     using System.Security;
18     using System.Security.Permissions;
19     using System.Diagnostics;
20     using System.Security.Principal;
21     using System.Security.Policy;
22     using System.Threading;
23     using System.Xml.Serialization.Configuration;
24     using System.Globalization;
25     using System.Runtime.Versioning;
26     using System.Runtime.CompilerServices;
27
28     internal class Compiler {
29         bool debugEnabled = DiagnosticsSwitches.KeepTempFiles.Enabled;
30         Hashtable imports = new Hashtable();
31         StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
32
33         [ResourceExposure(ResourceScope.Machine)]
34         protected string[] Imports {
35             get { 
36                 string[] array = new string[imports.Values.Count];
37                 imports.Values.CopyTo(array, 0);
38                 return array;
39             }
40         }
41
42         // SxS: This method does not take any resource name and does not expose any resources to the caller.
43         // It's OK to suppress the SxS warning.
44         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
45         [ResourceExposure(ResourceScope.None)]
46         internal void AddImport(Type type, Hashtable types) {
47             if (type == null)
48                 return;
49             if (TypeScope.IsKnownType(type))
50                 return;
51             if (types[type] != null)
52                 return;
53             types[type] = type;
54             Type baseType = type.BaseType;
55             if (baseType != null)
56                 AddImport(baseType, types);
57
58             Type declaringType = type.DeclaringType;
59             if (declaringType != null)
60                 AddImport(declaringType, types);
61
62             foreach (Type intf in type.GetInterfaces())
63                 AddImport(intf, types);
64
65             ConstructorInfo[] ctors = type.GetConstructors();
66             for (int i = 0; i < ctors.Length; i++) {
67                 ParameterInfo[] parms = ctors[i].GetParameters();
68                 for (int j = 0; j < parms.Length; j++) {
69                     AddImport(parms[j].ParameterType, types);
70                 }
71             }
72
73             if (type.IsGenericType) {
74                 Type[] arguments = type.GetGenericArguments();
75                 for (int i = 0; i < arguments.Length; i++) {
76                     AddImport(arguments[i], types);
77                 }
78             }
79
80             TempAssembly.FileIOPermission.Assert();
81             Module module = type.Module;
82             Assembly assembly = module.Assembly;
83             if (DynamicAssemblies.IsTypeDynamic(type)) {
84                 DynamicAssemblies.Add(assembly);
85                 return;
86             }
87
88             object[] typeForwardedFromAttribute = type.GetCustomAttributes(typeof(TypeForwardedFromAttribute), false);
89             if (typeForwardedFromAttribute.Length > 0)
90             {
91                 TypeForwardedFromAttribute originalAssemblyInfo = typeForwardedFromAttribute[0] as TypeForwardedFromAttribute;
92                 Assembly originalAssembly = Assembly.Load(originalAssemblyInfo.AssemblyFullName);
93                 imports[originalAssembly] = originalAssembly.Location;
94             }
95
96             imports[assembly] = assembly.Location;
97         }
98
99         // SxS: This method does not take any resource name and does not expose any resources to the caller.
100         // It's OK to suppress the SxS warning.
101         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
102         [ResourceExposure(ResourceScope.None)]
103         internal void AddImport(Assembly assembly) {
104             TempAssembly.FileIOPermission.Assert();
105             imports[assembly] = assembly.Location;
106         }
107
108         internal TextWriter Source {
109             get { return writer; }
110         }
111
112         internal void Close() { }
113
114         [ResourceConsumption(ResourceScope.Machine)]
115         [ResourceExposure(ResourceScope.Machine)]
116         internal static string GetTempAssemblyPath(string baseDir, Assembly assembly, string defaultNamespace) {
117             if (assembly.IsDynamic) {
118                 throw new InvalidOperationException(Res.GetString(Res.XmlPregenAssemblyDynamic));
119             }
120
121             PermissionSet perms = new PermissionSet(PermissionState.None);
122             perms.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
123             perms.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted));
124             perms.Assert();
125
126             try {
127                 if (baseDir != null && baseDir.Length > 0) {
128                     // check that the dirsctory exists
129                     if (!Directory.Exists(baseDir)) {
130                         throw new UnauthorizedAccessException(Res.GetString(Res.XmlPregenMissingDirectory, baseDir));
131                     }
132                 }
133                 else {
134                     baseDir = Path.GetTempPath();
135                     // check that the dirsctory exists
136                     if (!Directory.Exists(baseDir)) {
137                         throw new UnauthorizedAccessException(Res.GetString(Res.XmlPregenMissingTempDirectory));
138                     }
139                 }
140
141 #if MONO
142                 baseDir = Path.Combine (baseDir, GetTempAssemblyName(assembly.GetName(), defaultNamespace));
143 #else
144                 if (baseDir.EndsWith("\\", StringComparison.Ordinal))
145                     baseDir += GetTempAssemblyName(assembly.GetName(), defaultNamespace);
146                 else 
147                     baseDir += "\\" + GetTempAssemblyName(assembly.GetName(), defaultNamespace);
148 #endif
149             }
150             finally {
151                 CodeAccessPermission.RevertAssert();
152             }
153             return baseDir + ".dll";
154         }
155
156         internal static string GetTempAssemblyName(AssemblyName parent, string ns) {
157             return parent.Name + ".XmlSerializers" + (ns == null || ns.Length == 0 ? "" : "." +  ns.GetHashCode());
158         }
159
160         // SxS: This method does not take any resource name and does not expose any resources to the caller.
161         // It's OK to suppress the SxS warning.
162         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
163         [ResourceExposure(ResourceScope.None)]
164         internal Assembly Compile(Assembly parent, string ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence) {
165             CodeDomProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
166             CompilerParameters parameters = xmlParameters.CodeDomParameters;
167             parameters.ReferencedAssemblies.AddRange(Imports);
168             
169             if (debugEnabled) {
170                 parameters.GenerateInMemory = false;
171                 parameters.IncludeDebugInformation = true;
172                 parameters.TempFiles.KeepFiles = true;
173             }
174             PermissionSet perms = new PermissionSet(PermissionState.None);
175             if (xmlParameters.IsNeedTempDirAccess) {
176                 perms.AddPermission(TempAssembly.FileIOPermission);
177             }
178             perms.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted));
179             perms.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
180             perms.AddPermission(new SecurityPermission(SecurityPermissionFlag.ControlEvidence));
181             perms.Assert();
182
183             if (parent != null && (parameters.OutputAssembly == null || parameters.OutputAssembly.Length ==0)) {
184                 string assemblyName = AssemblyNameFromOptions(parameters.CompilerOptions);
185                 if (assemblyName == null)
186                     assemblyName = GetTempAssemblyPath(parameters.TempFiles.TempDir, parent, ns);
187                 // 
188                 parameters.OutputAssembly = assemblyName;
189             }
190
191             if (parameters.CompilerOptions == null || parameters.CompilerOptions.Length == 0)
192                 parameters.CompilerOptions = "/nostdlib";
193             else
194                 parameters.CompilerOptions += " /nostdlib";
195
196             parameters.CompilerOptions += " /D:_DYNAMIC_XMLSERIALIZER_COMPILATION";
197 #pragma warning disable 618
198             parameters.Evidence = evidence;
199 #pragma warning restore 618
200             CompilerResults results = null;
201             Assembly assembly = null;
202             try {
203                 results = codeProvider.CompileAssemblyFromSource(parameters, writer.ToString());
204                 // check the output for errors or a certain level-1 warning (1595)
205                 if (results.Errors.Count > 0) {
206                     StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
207                     stringWriter.WriteLine(Res.GetString(Res.XmlCompilerError, results.NativeCompilerReturnValue.ToString(CultureInfo.InvariantCulture)));
208                     bool foundOne = false;
209                     foreach (CompilerError e in results.Errors) {
210                         // clear filename. This makes ToString() print just error number and message.
211                         e.FileName = "";
212                         if (!e.IsWarning || e.ErrorNumber == "CS1595") {
213                             foundOne = true;
214                             stringWriter.WriteLine(e.ToString());
215                         }
216                     }
217                     if (foundOne) {
218                         throw new InvalidOperationException(stringWriter.ToString());
219                     }
220                 }
221                 assembly = results.CompiledAssembly;
222             }
223             catch (UnauthorizedAccessException) {
224                 // try to get the user token
225                 string user = GetCurrentUser();
226                 if (user == null || user.Length == 0) {
227                     throw new UnauthorizedAccessException(Res.GetString(Res.XmlSerializerAccessDenied));
228                 }
229                 else {
230                     throw new UnauthorizedAccessException(Res.GetString(Res.XmlIdentityAccessDenied, user));
231                 }
232             }
233             catch (FileLoadException fle) {
234                 throw new InvalidOperationException(Res.GetString(Res.XmlSerializerCompileFailed), fle);
235             }
236             finally {
237                 CodeAccessPermission.RevertAssert();
238             }
239             // somehow we got here without generating an assembly
240             if (assembly == null) throw new InvalidOperationException(Res.GetString(Res.XmlInternalError));
241             
242             return assembly;
243         }
244
245         static string AssemblyNameFromOptions(string options) {
246             if (options == null || options.Length == 0)
247                 return null;
248
249             string outName = null;
250             string[] flags = options.ToLower(CultureInfo.InvariantCulture).Split(null);
251             for (int i = 0; i < flags.Length; i++) {
252                 string val = flags[i].Trim();
253                 if (val.StartsWith("/out:", StringComparison.Ordinal)) {
254                     outName = val.Substring(5);
255                 }
256             }
257             return outName;
258         }
259
260         internal static string GetCurrentUser()
261         {
262 #if !FEATURE_PAL
263             try {
264                 WindowsIdentity id = WindowsIdentity.GetCurrent();
265                 if (id != null && id.Name != null)
266                     return id.Name;
267             } 
268             catch (Exception e) {
269                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
270                     throw;
271                 }
272             }
273 #endif // !FEATURE_PAL
274             return "";
275         }
276     }
277 }
278
279