Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting / PlatformAdaptationLayer.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if FEATURE_CORE_DLR
17 using System.Linq.Expressions;
18 #else
19 using Microsoft.Scripting.Ast;
20 #endif
21
22 using System;
23 using System.Collections.Generic;
24 using System.IO;
25 using System.Reflection;
26 using System.Runtime.InteropServices;
27 using System.Security;
28 using Microsoft.Scripting.Utils;
29 using System.Runtime.CompilerServices;
30 using System.Collections;
31
32 namespace Microsoft.Scripting {
33
34 #if !FEATURE_PROCESS
35     public class ExitProcessException : Exception {
36
37         public int ExitCode { get { return exitCode; } }
38         int exitCode;
39
40         public ExitProcessException(int exitCode) {
41             this.exitCode = exitCode;
42         }
43     }
44 #endif
45
46     /// <summary>
47     /// Abstracts system operations that are used by DLR and could potentially be platform specific.
48     /// The host can implement its PAL to adapt DLR to the platform it is running on.
49     /// For example, the Silverlight host adapts some file operations to work against files on the server.
50     /// </summary>
51     [Serializable]
52     public class PlatformAdaptationLayer {
53         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
54         public static readonly PlatformAdaptationLayer Default = new PlatformAdaptationLayer();
55
56         public static readonly bool IsCompactFramework =
57 #if WIN8
58             false;
59 #else
60             Environment.OSVersion.Platform == PlatformID.WinCE ||
61             Environment.OSVersion.Platform == PlatformID.Xbox;
62 #endif
63
64 #if SILVERLIGHT
65
66         // this dictionary is readonly after initialization:
67         private Dictionary<string, string> _assemblyFullNames = new Dictionary<string, string>();
68
69         public PlatformAdaptationLayer() {
70             LoadSilverlightAssemblyNameMapping();
71         }
72
73         // TODO: remove the need for this
74         private void LoadSilverlightAssemblyNameMapping() {
75             // non-trasparent assemblies
76             AssemblyName platformKeyVer = new AssemblyName(typeof(object).Assembly.FullName);
77             AddAssemblyMappings(platformKeyVer,
78                 "mscorlib",
79                 "System",
80                 "System.Core",
81                 "System.Net",
82                 "System.Runtime.Serialization",
83                 "System.ServiceModel.Web",
84                 "System.Windows",
85                 "System.Windows.Browser",
86                 "System.Xml",
87                 "Microsoft.VisualBasic"
88             );
89
90             // DLR + language assemblies
91             AssemblyName languageKeyVer = new AssemblyName(typeof(PlatformAdaptationLayer).Assembly.FullName);
92             AddAssemblyMappings(languageKeyVer, 
93                 "Microsoft.Scripting",
94                 "Microsoft.Dynamic",
95                 "Microsoft.Scripting.Core",
96                 "Microsoft.Scripting.Silverlight",
97                 "IronPython",
98                 "IronPython.Modules",
99                 "IronRuby",
100                 "IronRuby.Libraries"
101             );
102
103             // transparent assemblies => same version as mscorlib but uses transparent key (same as languages)
104             AssemblyName transparentKeyVer = new AssemblyName(typeof(object).Assembly.FullName);
105             transparentKeyVer.SetPublicKeyToken(languageKeyVer.GetPublicKeyToken());
106             AddAssemblyMappings(transparentKeyVer,
107                 "System.ServiceModel",
108                 "System.ServiceModel.Syndication",
109                 "System.Windows.Controls",
110                 "System.Windows.Controls.Data",
111                 "System.Windows.Controls.Data.Design",
112                 "System.Windows.Controls.Design",
113                 "System.Windows.Controls.Extended",
114                 "System.Windows.Controls.Extended.Design",
115                 "System.Xml.Linq",
116                 "System.Xml.Serialization"
117             );
118         }
119
120         private void AddAssemblyMappings(AssemblyName keyVersion, params string[] names) {
121             foreach (string asm in names) {
122                 keyVersion.Name = asm;
123                 _assemblyFullNames.Add(asm.ToLower(), keyVersion.FullName);
124             }
125         }
126
127         protected string LookupFullName(string name) {
128             AssemblyName asm = new AssemblyName(name);
129             if (asm.Version != null || asm.GetPublicKeyToken() != null || asm.GetPublicKey() != null) {
130                 return name;
131             }
132             return _assemblyFullNames.ContainsKey(name.ToLower()) ? _assemblyFullNames[name.ToLower()] : name;
133         }
134 #endif
135         #region Assembly Loading
136
137         public virtual Assembly LoadAssembly(string name) {
138 #if WIN8
139             throw new NotImplementedException();
140 #elif !SILVERLIGHT
141             return Assembly.Load(name);
142 #else
143             return Assembly.Load(LookupFullName(name));
144 #endif
145         }
146
147         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFile")]
148         public virtual Assembly LoadAssemblyFromPath(string path) {
149 #if FEATURE_FILESYSTEM
150             return Assembly.LoadFile(path);
151 #else
152             throw new NotImplementedException();
153 #endif
154         }
155
156         public virtual void TerminateScriptExecution(int exitCode) {
157 #if FEATURE_PROCESS
158             System.Environment.Exit(exitCode);
159 #else
160             throw new ExitProcessException(exitCode);
161 #endif
162         }
163
164         #endregion
165
166         #region Virtual File System
167
168         public virtual bool IsSingleRootFileSystem {
169             get {
170 #if FEATURE_FILESYSTEM
171                 return Environment.OSVersion.Platform == PlatformID.Unix
172                     || Environment.OSVersion.Platform == PlatformID.MacOSX;
173 #elif WIN8
174                 return false;
175 #else
176                 return true;
177 #endif
178             }
179         }
180
181         public virtual StringComparer PathComparer {
182             get {
183 #if FEATURE_FILESYSTEM
184                 return Environment.OSVersion.Platform == PlatformID.Unix ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
185 #else
186                 return StringComparer.OrdinalIgnoreCase;
187 #endif
188             }
189         }
190
191         public virtual bool FileExists(string path) {
192 #if FEATURE_FILESYSTEM
193             return File.Exists(path);
194 #else
195             throw new NotImplementedException();
196 #endif
197         }
198
199         public virtual bool DirectoryExists(string path) {
200 #if FEATURE_FILESYSTEM
201             return Directory.Exists(path);
202 #else
203             throw new NotImplementedException();
204 #endif
205         }
206
207 #if !CLR2
208         // TODO: better APIs
209         public virtual Stream OpenFileStream(string path, FileMode mode = FileMode.OpenOrCreate, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.Read, int bufferSize = 8192) {
210 #if FEATURE_FILESYSTEM
211             if (string.Equals("nul", path, StringComparison.InvariantCultureIgnoreCase)) {
212                 return Stream.Null;
213             }
214             return new FileStream(path, mode, access, share, bufferSize);
215 #else
216             throw new NotImplementedException();
217 #endif
218         }
219
220         // TODO: better APIs
221         public virtual Stream OpenInputFileStream(string path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read, FileShare share = FileShare.Read, int bufferSize = 8192) {
222             return OpenFileStream(path, mode, access, share, bufferSize);
223         }
224
225         // TODO: better APIs
226         public virtual Stream OpenOutputFileStream(string path) {
227             return OpenFileStream(path, FileMode.Create, FileAccess.Write);
228         }
229 #else
230         public virtual Stream OpenFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) {
231 #if FEATURE_FILESYSTEM
232             if (string.Equals("nul", path, StringComparison.InvariantCultureIgnoreCase)) {
233                 return Stream.Null;
234             }
235             return new FileStream(path, mode, access, share, bufferSize);
236 #else
237             throw new NotImplementedException();
238 #endif
239         }
240
241         // TODO: better APIs
242         public virtual Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share) {
243             return OpenFileStream(path, mode, access, share, 8912);
244         }
245
246         // TODO: better APIs
247         public virtual Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) {
248             return OpenFileStream(path, mode, access, share, bufferSize);
249         }
250
251         // TODO: better APIs
252         public virtual Stream OpenInputFileStream(string path) {
253             return OpenFileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 8912);
254         }
255
256         // TODO: better APIs
257         public virtual Stream OpenOutputFileStream(string path) {
258             return OpenFileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 8912);
259         }
260 #endif
261
262         public virtual void DeleteFile(string path, bool deleteReadOnly) {
263 #if FEATURE_FILESYSTEM
264             FileInfo info = new FileInfo(path);
265 #if !ANDROID
266             if (deleteReadOnly && info.IsReadOnly) {
267                 info.IsReadOnly = false;
268             }
269 #endif
270             info.Delete();
271 #else
272             throw new NotImplementedException();
273 #endif
274         }
275
276         public string[] GetFiles(string path, string searchPattern) {
277             return GetFileSystemEntries(path, searchPattern, true, false);
278         }
279
280         public string[] GetDirectories(string path, string searchPattern) {
281             return GetFileSystemEntries(path, searchPattern, false, true);
282         }
283
284         public string[] GetFileSystemEntries(string path, string searchPattern) {
285             return GetFileSystemEntries(path, searchPattern, true, true);
286         }
287
288         public virtual string[] GetFileSystemEntries(string path, string searchPattern, bool includeFiles, bool includeDirectories) {
289 #if FEATURE_FILESYSTEM
290             if (includeFiles && includeDirectories) {
291                 return Directory.GetFileSystemEntries(path, searchPattern);
292             }
293             if (includeFiles) {
294                 return Directory.GetFiles(path, searchPattern);
295             }
296             if (includeDirectories) {
297                 return Directory.GetDirectories(path, searchPattern);
298             }
299             return ArrayUtils.EmptyStrings;
300 #else
301             throw new NotImplementedException();
302 #endif
303         }
304
305         /// <exception cref="ArgumentException">Invalid path.</exception>
306         public virtual string GetFullPath(string path) {
307 #if FEATURE_FILESYSTEM
308             try {
309                 return Path.GetFullPath(path);
310             } catch (Exception) {
311                 throw Error.InvalidPath();
312             }
313 #else
314             throw new NotImplementedException();
315 #endif
316         }
317
318         public virtual string CombinePaths(string path1, string path2) {
319             return Path.Combine(path1, path2);
320         }
321
322         public virtual string GetFileName(string path) {
323             return Path.GetFileName(path);
324         }
325
326         public virtual string GetDirectoryName(string path) {
327             return Path.GetDirectoryName(path);
328         }
329
330         public virtual string GetExtension(string path) {
331             return Path.GetExtension(path);
332         }
333
334         public virtual string GetFileNameWithoutExtension(string path) {
335             return Path.GetFileNameWithoutExtension(path);
336         }
337
338         /// <exception cref="ArgumentException">Invalid path.</exception>
339         public virtual bool IsAbsolutePath(string path) {
340             if (String.IsNullOrEmpty(path)) {
341                 return false;
342             }
343
344             // no drives, no UNC:
345             if (IsSingleRootFileSystem) {
346                 return IsDirectorySeparator(path[0]);
347             }
348
349             if (IsDirectorySeparator(path[0])) {
350                 // UNC path
351                 return path.Length > 1 && IsDirectorySeparator(path[1]);
352             }
353
354             if (path.Length > 2 && path[1] == ':' && IsDirectorySeparator(path[2])) {
355                 return true;
356             }
357
358             return false;
359         }
360
361 #if FEATURE_FILESYSTEM
362         private bool IsDirectorySeparator(char c) {
363             return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar;
364         }
365 #else
366         private bool IsDirectorySeparator(char c) {
367             return c == '\\' || c == '/';
368         }
369 #endif
370
371         public virtual string CurrentDirectory {
372             get {
373 #if FEATURE_FILESYSTEM
374                 return Directory.GetCurrentDirectory();
375 #else
376                 throw new NotImplementedException();
377 #endif
378             }
379             set {
380 #if FEATURE_FILESYSTEM
381                 Directory.SetCurrentDirectory(value);
382 #else
383                 throw new NotImplementedException();
384 #endif
385             }
386         }
387
388         public virtual void CreateDirectory(string path) {
389 #if FEATURE_FILESYSTEM
390             Directory.CreateDirectory(path);
391 #else
392             throw new NotImplementedException();
393 #endif
394         }
395
396         public virtual void DeleteDirectory(string path, bool recursive) {
397 #if FEATURE_FILESYSTEM
398             Directory.Delete(path, recursive);
399 #else
400             throw new NotImplementedException();
401 #endif
402         }
403
404         public virtual void MoveFileSystemEntry(string sourcePath, string destinationPath) {
405 #if FEATURE_FILESYSTEM
406             Directory.Move(sourcePath, destinationPath);
407 #else
408             throw new NotImplementedException();
409 #endif
410         }
411
412         #endregion
413
414         #region Environmental Variables
415
416         public virtual string GetEnvironmentVariable(string key) {
417 #if FEATURE_PROCESS
418             return Environment.GetEnvironmentVariable(key);
419 #else
420             throw new NotImplementedException();
421 #endif
422         }
423
424         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
425         public virtual void SetEnvironmentVariable(string key, string value) {
426 #if FEATURE_PROCESS
427             if (value != null && value.Length == 0) {
428                 SetEmptyEnvironmentVariable(key);
429             } else {
430                 Environment.SetEnvironmentVariable(key, value);
431             }
432 #else
433             throw new NotImplementedException();
434 #endif
435         }
436
437 #if FEATURE_PROCESS
438         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
439         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2149:TransparentMethodsMustNotCallNativeCodeFxCopRule")]
440         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCodeFxCopRule")]
441         [MethodImpl(MethodImplOptions.NoInlining)]
442         private static void SetEmptyEnvironmentVariable(string key) {
443             // System.Environment.SetEnvironmentVariable interprets an empty value string as 
444             // deleting the environment variable. So we use the native SetEnvironmentVariable 
445             // function here which allows setting of the value to an empty string.
446             // This will require high trust and will fail in sandboxed environments
447             if (!NativeMethods.SetEnvironmentVariable(key, String.Empty)) {
448                 throw new ExternalException("SetEnvironmentVariable failed", Marshal.GetLastWin32Error());
449             }
450         }
451 #endif
452
453         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
454         public virtual Dictionary<string, string> GetEnvironmentVariables() {
455 #if FEATURE_PROCESS
456             var result = new Dictionary<string, string>();
457
458             foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
459             {
460                 result.Add((string)entry.Key, (string)entry.Value);
461             }
462
463             return result;
464 #else
465             throw new NotImplementedException();
466 #endif
467         }
468
469         #endregion
470     }
471 }