PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Runtime/DlrConfiguration.cs

https://bitbucket.org/stefanrusek/xronos
C# | 328 lines | 229 code | 58 blank | 41 comment | 26 complexity | b43131bc005610a38554788b41e9ce07 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. using System.Reflection;
  23. using System.Threading;
  24. using Microsoft.Scripting.Utils;
  25. namespace Microsoft.Scripting.Runtime {
  26. /// <summary>
  27. /// Singleton for each language.
  28. /// </summary>
  29. internal sealed class LanguageConfiguration {
  30. private readonly AssemblyQualifiedTypeName _providerName;
  31. private readonly string _displayName;
  32. private readonly IDictionary<string, object> _options;
  33. private LanguageContext _context;
  34. public LanguageContext LanguageContext {
  35. get { return _context; }
  36. }
  37. public AssemblyQualifiedTypeName ProviderName {
  38. get { return _providerName; }
  39. }
  40. public string DisplayName {
  41. get { return _displayName; }
  42. }
  43. public LanguageConfiguration(AssemblyQualifiedTypeName providerName, string displayName, IDictionary<string, object> options) {
  44. _providerName = providerName;
  45. _displayName = displayName;
  46. _options = options;
  47. }
  48. /// <summary>
  49. /// Must not be called under a lock as it can potentially call a user code.
  50. /// </summary>
  51. /// <exception cref="MissingTypeException"></exception>
  52. /// <exception cref="Microsoft.Scripting.InvalidImplementationException">The language context's implementation failed to instantiate.</exception>
  53. internal LanguageContext LoadLanguageContext(ScriptDomainManager domainManager, out bool alreadyLoaded) {
  54. if (_context == null) {
  55. // Let assembly load errors bubble out
  56. var assembly = domainManager.Platform.LoadAssembly(_providerName.AssemblyName.FullName);
  57. Type type = assembly.GetType(_providerName.TypeName);
  58. if (type == null) {
  59. throw new InvalidOperationException(
  60. string.Format(
  61. "Failed to load language '{0}': assembly '{1}' does not contain type '{2}'",
  62. _displayName, assembly.Location, _providerName.TypeName
  63. ));
  64. }
  65. if (!type.IsSubclassOf(typeof(LanguageContext))) {
  66. throw new InvalidOperationException(
  67. string.Format(
  68. "Failed to load language '{0}': type '{1}' is not a valid language provider because it does not inherit from LanguageContext",
  69. _displayName, type
  70. ));
  71. }
  72. var context = ReflectionUtils.CreateInstance<LanguageContext>(type, domainManager, _options);
  73. alreadyLoaded = Interlocked.CompareExchange(ref _context, context, null) != null;
  74. } else {
  75. alreadyLoaded = true;
  76. }
  77. return _context;
  78. }
  79. }
  80. public sealed class DlrConfiguration {
  81. private bool _frozen;
  82. private readonly bool _debugMode;
  83. private readonly bool _privateBinding;
  84. private readonly IDictionary<string, object> _options;
  85. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  86. public static readonly StringComparer FileExtensionComparer = StringComparer.OrdinalIgnoreCase;
  87. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  88. public static readonly StringComparer LanguageNameComparer = StringComparer.OrdinalIgnoreCase;
  89. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  90. public static readonly StringComparer OptionNameComparer = StringComparer.Ordinal;
  91. private readonly Dictionary<string, LanguageConfiguration> _languageNames;
  92. private readonly Dictionary<string, LanguageConfiguration> _languageExtensions;
  93. private readonly Dictionary<AssemblyQualifiedTypeName, LanguageConfiguration> _languageConfigurations;
  94. private readonly Dictionary<Type, LanguageConfiguration> _loadedProviderTypes;
  95. public DlrConfiguration(bool debugMode, bool privateBinding, IDictionary<string, object> options) {
  96. ContractUtils.RequiresNotNull(options, "options");
  97. _debugMode = debugMode;
  98. _privateBinding = privateBinding;
  99. _options = options;
  100. _languageNames = new Dictionary<string, LanguageConfiguration>(LanguageNameComparer);
  101. _languageExtensions = new Dictionary<string, LanguageConfiguration>(FileExtensionComparer);
  102. _languageConfigurations = new Dictionary<AssemblyQualifiedTypeName, LanguageConfiguration>();
  103. _loadedProviderTypes = new Dictionary<Type, LanguageConfiguration>();
  104. }
  105. /// <summary>
  106. /// Whether the application is in debug mode.
  107. /// This means:
  108. ///
  109. /// 1) Symbols are emitted for debuggable methods (methods associated with SourceUnit).
  110. /// 2) Debuggable methods are emitted to non-collectable types (this is due to CLR limitations on dynamic method debugging).
  111. /// 3) JIT optimization is disabled for all methods
  112. /// 4) Languages may disable optimizations based on this value.
  113. /// </summary>
  114. public bool DebugMode {
  115. get { return _debugMode; }
  116. }
  117. /// <summary>
  118. /// Ignore CLR visibility checks.
  119. /// </summary>
  120. public bool PrivateBinding {
  121. get { return _privateBinding; }
  122. }
  123. internal IDictionary<string, object> Options {
  124. get { return _options; }
  125. }
  126. internal IDictionary<AssemblyQualifiedTypeName, LanguageConfiguration> Languages {
  127. get { return _languageConfigurations; }
  128. }
  129. public void AddLanguage(string languageTypeName, string displayName, IList<string> names, IList<string> fileExtensions,
  130. IDictionary<string, object> options) {
  131. AddLanguage(languageTypeName, displayName, names, fileExtensions, options, null);
  132. }
  133. internal void AddLanguage(string languageTypeName, string displayName, IList<string> names, IList<string> fileExtensions,
  134. IDictionary<string, object> options, string paramName) {
  135. ContractUtils.Requires(!_frozen, "Configuration cannot be modified once the runtime is initialized");
  136. ContractUtils.Requires(
  137. CollectionUtils.TrueForAll(names, (id) => !String.IsNullOrEmpty(id) && !_languageNames.ContainsKey(id)),
  138. paramName ?? "names",
  139. "Language name should not be null, empty or duplicated between languages"
  140. );
  141. ContractUtils.Requires(
  142. CollectionUtils.TrueForAll(fileExtensions, (ext) => !String.IsNullOrEmpty(ext) && !_languageExtensions.ContainsKey(ext)),
  143. paramName ?? "fileExtensions",
  144. "File extension should not be null, empty or duplicated between languages"
  145. );
  146. ContractUtils.RequiresNotNull(displayName, paramName ?? "displayName");
  147. if (string.IsNullOrEmpty(displayName)) {
  148. ContractUtils.Requires(names.Count > 0, paramName ?? "displayName", "Must have a non-empty display name or a a non-empty list of language names");
  149. displayName = names[0];
  150. }
  151. var aqtn = AssemblyQualifiedTypeName.ParseArgument(languageTypeName, paramName ?? "languageTypeName");
  152. if (_languageConfigurations.ContainsKey(aqtn)) {
  153. throw new ArgumentException(string.Format("Duplicate language with type name '{0}'", aqtn), "languageTypeName");
  154. }
  155. // Add global language options first, they can be rewritten by language specific ones:
  156. var mergedOptions = new Dictionary<string, object>(_options);
  157. // Replace global options with language-specific options
  158. foreach (var option in options) {
  159. mergedOptions[option.Key] = option.Value;
  160. }
  161. var config = new LanguageConfiguration(aqtn, displayName, mergedOptions);
  162. _languageConfigurations.Add(aqtn, config);
  163. // allow duplicate ids in identifiers and extensions lists:
  164. foreach (var name in names) {
  165. _languageNames[name] = config;
  166. }
  167. foreach (var ext in fileExtensions) {
  168. _languageExtensions[NormalizeExtension(ext)] = config;
  169. }
  170. }
  171. internal static string NormalizeExtension(string extension) {
  172. return extension[0] == '.' ? extension : "." + extension;
  173. }
  174. internal void Freeze() {
  175. Debug.Assert(!_frozen);
  176. _frozen = true;
  177. }
  178. internal bool TryLoadLanguage(ScriptDomainManager manager, AssemblyQualifiedTypeName providerName, out LanguageContext language) {
  179. Assert.NotNull(manager);
  180. LanguageConfiguration config;
  181. if (_languageConfigurations.TryGetValue(providerName, out config)) {
  182. language = LoadLanguageContext(manager, config);
  183. return true;
  184. }
  185. language = null;
  186. return false;
  187. }
  188. internal bool TryLoadLanguage(ScriptDomainManager manager, string str, bool isExtension, out LanguageContext language) {
  189. Assert.NotNull(manager, str);
  190. var dict = (isExtension) ? _languageExtensions : _languageNames;
  191. LanguageConfiguration config;
  192. if (dict.TryGetValue(str, out config)) {
  193. language = LoadLanguageContext(manager, config);
  194. return true;
  195. }
  196. language = null;
  197. return false;
  198. }
  199. private LanguageContext LoadLanguageContext(ScriptDomainManager manager, LanguageConfiguration config) {
  200. bool alreadyLoaded;
  201. var language = config.LoadLanguageContext(manager, out alreadyLoaded);
  202. if (!alreadyLoaded) {
  203. // Checks whether a single language is not registered under two different AQTNs.
  204. // We can only do it now because there is no way how to ensure that two AQTNs don't refer to the same type w/o loading the type.
  205. // The check takes place after config.LoadLanguageContext is called to avoid calling user code while holding a lock.
  206. lock (_loadedProviderTypes) {
  207. LanguageConfiguration existingConfig;
  208. Type type = language.GetType();
  209. if (_loadedProviderTypes.TryGetValue(type, out existingConfig)) {
  210. throw new InvalidOperationException(String.Format("Language implemented by type '{0}' has already been loaded using name '{1}'",
  211. config.ProviderName, existingConfig.ProviderName));
  212. }
  213. _loadedProviderTypes.Add(type, config);
  214. }
  215. }
  216. return language;
  217. }
  218. public string[] GetLanguageNames(LanguageContext context) {
  219. ContractUtils.RequiresNotNull(context, "context");
  220. List<string> result = new List<string>();
  221. foreach (var entry in _languageNames) {
  222. if (entry.Value.LanguageContext == context) {
  223. result.Add(entry.Key);
  224. }
  225. }
  226. return result.ToArray();
  227. }
  228. internal string[] GetLanguageNames(LanguageConfiguration config) {
  229. List<string> result = new List<string>();
  230. foreach (var entry in _languageNames) {
  231. if (entry.Value == config) {
  232. result.Add(entry.Key);
  233. }
  234. }
  235. return result.ToArray();
  236. }
  237. public string[] GetLanguageNames() {
  238. return ArrayUtils.MakeArray<string>(_languageNames.Keys);
  239. }
  240. public string[] GetFileExtensions(LanguageContext context) {
  241. var result = new List<string>();
  242. foreach (var entry in _languageExtensions) {
  243. if (entry.Value.LanguageContext == context) {
  244. result.Add(entry.Key);
  245. }
  246. }
  247. return result.ToArray();
  248. }
  249. internal string[] GetFileExtensions(LanguageConfiguration config) {
  250. var result = new List<string>();
  251. foreach (var entry in _languageExtensions) {
  252. if (entry.Value == config) {
  253. result.Add(entry.Key);
  254. }
  255. }
  256. return result.ToArray();
  257. }
  258. public string[] GetFileExtensions() {
  259. return ArrayUtils.MakeArray<string>(_languageExtensions.Keys);
  260. }
  261. internal LanguageConfiguration GetLanguageConfig(LanguageContext context) {
  262. foreach (var config in _languageConfigurations.Values) {
  263. if (config.LanguageContext == context) {
  264. return config;
  265. }
  266. }
  267. return null;
  268. }
  269. }
  270. }