/Microsoft.Scripting/Runtime/DlrConfiguration.cs
C# | 328 lines | 229 code | 58 blank | 41 comment | 26 complexity | b43131bc005610a38554788b41e9ce07 MD5 | raw file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
-
- #if CODEPLEX_40
- using System;
- #else
- using System; using Microsoft;
- #endif
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection;
- using System.Threading;
- using Microsoft.Scripting.Utils;
-
- namespace Microsoft.Scripting.Runtime {
- /// <summary>
- /// Singleton for each language.
- /// </summary>
- internal sealed class LanguageConfiguration {
- private readonly AssemblyQualifiedTypeName _providerName;
- private readonly string _displayName;
- private readonly IDictionary<string, object> _options;
- private LanguageContext _context;
-
- public LanguageContext LanguageContext {
- get { return _context; }
- }
-
- public AssemblyQualifiedTypeName ProviderName {
- get { return _providerName; }
- }
-
- public string DisplayName {
- get { return _displayName; }
- }
-
- public LanguageConfiguration(AssemblyQualifiedTypeName providerName, string displayName, IDictionary<string, object> options) {
- _providerName = providerName;
- _displayName = displayName;
- _options = options;
- }
-
- /// <summary>
- /// Must not be called under a lock as it can potentially call a user code.
- /// </summary>
- /// <exception cref="MissingTypeException"></exception>
- /// <exception cref="Microsoft.Scripting.InvalidImplementationException">The language context's implementation failed to instantiate.</exception>
- internal LanguageContext LoadLanguageContext(ScriptDomainManager domainManager, out bool alreadyLoaded) {
- if (_context == null) {
-
- // Let assembly load errors bubble out
- var assembly = domainManager.Platform.LoadAssembly(_providerName.AssemblyName.FullName);
-
- Type type = assembly.GetType(_providerName.TypeName);
- if (type == null) {
- throw new InvalidOperationException(
- string.Format(
- "Failed to load language '{0}': assembly '{1}' does not contain type '{2}'",
- _displayName, assembly.Location, _providerName.TypeName
- ));
- }
-
- if (!type.IsSubclassOf(typeof(LanguageContext))) {
- throw new InvalidOperationException(
- string.Format(
- "Failed to load language '{0}': type '{1}' is not a valid language provider because it does not inherit from LanguageContext",
- _displayName, type
- ));
- }
-
- var context = ReflectionUtils.CreateInstance<LanguageContext>(type, domainManager, _options);
- alreadyLoaded = Interlocked.CompareExchange(ref _context, context, null) != null;
- } else {
- alreadyLoaded = true;
- }
-
- return _context;
- }
- }
-
- public sealed class DlrConfiguration {
- private bool _frozen;
-
- private readonly bool _debugMode;
- private readonly bool _privateBinding;
- private readonly IDictionary<string, object> _options;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly StringComparer FileExtensionComparer = StringComparer.OrdinalIgnoreCase;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly StringComparer LanguageNameComparer = StringComparer.OrdinalIgnoreCase;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
- public static readonly StringComparer OptionNameComparer = StringComparer.Ordinal;
-
- private readonly Dictionary<string, LanguageConfiguration> _languageNames;
- private readonly Dictionary<string, LanguageConfiguration> _languageExtensions;
- private readonly Dictionary<AssemblyQualifiedTypeName, LanguageConfiguration> _languageConfigurations;
- private readonly Dictionary<Type, LanguageConfiguration> _loadedProviderTypes;
-
- public DlrConfiguration(bool debugMode, bool privateBinding, IDictionary<string, object> options) {
- ContractUtils.RequiresNotNull(options, "options");
- _debugMode = debugMode;
- _privateBinding = privateBinding;
- _options = options;
-
- _languageNames = new Dictionary<string, LanguageConfiguration>(LanguageNameComparer);
- _languageExtensions = new Dictionary<string, LanguageConfiguration>(FileExtensionComparer);
- _languageConfigurations = new Dictionary<AssemblyQualifiedTypeName, LanguageConfiguration>();
- _loadedProviderTypes = new Dictionary<Type, LanguageConfiguration>();
- }
-
- /// <summary>
- /// Whether the application is in debug mode.
- /// This means:
- ///
- /// 1) Symbols are emitted for debuggable methods (methods associated with SourceUnit).
- /// 2) Debuggable methods are emitted to non-collectable types (this is due to CLR limitations on dynamic method debugging).
- /// 3) JIT optimization is disabled for all methods
- /// 4) Languages may disable optimizations based on this value.
- /// </summary>
- public bool DebugMode {
- get { return _debugMode; }
- }
-
- /// <summary>
- /// Ignore CLR visibility checks.
- /// </summary>
- public bool PrivateBinding {
- get { return _privateBinding; }
- }
-
- internal IDictionary<string, object> Options {
- get { return _options; }
- }
-
- internal IDictionary<AssemblyQualifiedTypeName, LanguageConfiguration> Languages {
- get { return _languageConfigurations; }
- }
-
- public void AddLanguage(string languageTypeName, string displayName, IList<string> names, IList<string> fileExtensions,
- IDictionary<string, object> options) {
- AddLanguage(languageTypeName, displayName, names, fileExtensions, options, null);
- }
-
- internal void AddLanguage(string languageTypeName, string displayName, IList<string> names, IList<string> fileExtensions,
- IDictionary<string, object> options, string paramName) {
- ContractUtils.Requires(!_frozen, "Configuration cannot be modified once the runtime is initialized");
- ContractUtils.Requires(
- CollectionUtils.TrueForAll(names, (id) => !String.IsNullOrEmpty(id) && !_languageNames.ContainsKey(id)),
- paramName ?? "names",
- "Language name should not be null, empty or duplicated between languages"
- );
- ContractUtils.Requires(
- CollectionUtils.TrueForAll(fileExtensions, (ext) => !String.IsNullOrEmpty(ext) && !_languageExtensions.ContainsKey(ext)),
- paramName ?? "fileExtensions",
- "File extension should not be null, empty or duplicated between languages"
- );
- ContractUtils.RequiresNotNull(displayName, paramName ?? "displayName");
-
- if (string.IsNullOrEmpty(displayName)) {
- ContractUtils.Requires(names.Count > 0, paramName ?? "displayName", "Must have a non-empty display name or a a non-empty list of language names");
- displayName = names[0];
- }
-
- var aqtn = AssemblyQualifiedTypeName.ParseArgument(languageTypeName, paramName ?? "languageTypeName");
- if (_languageConfigurations.ContainsKey(aqtn)) {
- throw new ArgumentException(string.Format("Duplicate language with type name '{0}'", aqtn), "languageTypeName");
- }
-
- // Add global language options first, they can be rewritten by language specific ones:
- var mergedOptions = new Dictionary<string, object>(_options);
-
- // Replace global options with language-specific options
- foreach (var option in options) {
- mergedOptions[option.Key] = option.Value;
- }
-
- var config = new LanguageConfiguration(aqtn, displayName, mergedOptions);
-
- _languageConfigurations.Add(aqtn, config);
-
- // allow duplicate ids in identifiers and extensions lists:
- foreach (var name in names) {
- _languageNames[name] = config;
- }
-
- foreach (var ext in fileExtensions) {
- _languageExtensions[NormalizeExtension(ext)] = config;
- }
- }
-
- internal static string NormalizeExtension(string extension) {
- return extension[0] == '.' ? extension : "." + extension;
- }
-
- internal void Freeze() {
- Debug.Assert(!_frozen);
- _frozen = true;
- }
-
- internal bool TryLoadLanguage(ScriptDomainManager manager, AssemblyQualifiedTypeName providerName, out LanguageContext language) {
- Assert.NotNull(manager);
- LanguageConfiguration config;
-
- if (_languageConfigurations.TryGetValue(providerName, out config)) {
- language = LoadLanguageContext(manager, config);
- return true;
- }
-
- language = null;
- return false;
- }
-
- internal bool TryLoadLanguage(ScriptDomainManager manager, string str, bool isExtension, out LanguageContext language) {
- Assert.NotNull(manager, str);
-
- var dict = (isExtension) ? _languageExtensions : _languageNames;
-
- LanguageConfiguration config;
- if (dict.TryGetValue(str, out config)) {
- language = LoadLanguageContext(manager, config);
- return true;
- }
-
- language = null;
- return false;
- }
-
- private LanguageContext LoadLanguageContext(ScriptDomainManager manager, LanguageConfiguration config) {
- bool alreadyLoaded;
- var language = config.LoadLanguageContext(manager, out alreadyLoaded);
-
- if (!alreadyLoaded) {
- // Checks whether a single language is not registered under two different AQTNs.
- // 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.
- // The check takes place after config.LoadLanguageContext is called to avoid calling user code while holding a lock.
- lock (_loadedProviderTypes) {
- LanguageConfiguration existingConfig;
- Type type = language.GetType();
- if (_loadedProviderTypes.TryGetValue(type, out existingConfig)) {
- throw new InvalidOperationException(String.Format("Language implemented by type '{0}' has already been loaded using name '{1}'",
- config.ProviderName, existingConfig.ProviderName));
- }
-
- _loadedProviderTypes.Add(type, config);
- }
- }
- return language;
- }
-
- public string[] GetLanguageNames(LanguageContext context) {
- ContractUtils.RequiresNotNull(context, "context");
-
- List<string> result = new List<string>();
-
- foreach (var entry in _languageNames) {
- if (entry.Value.LanguageContext == context) {
- result.Add(entry.Key);
- }
- }
-
- return result.ToArray();
- }
-
- internal string[] GetLanguageNames(LanguageConfiguration config) {
- List<string> result = new List<string>();
-
- foreach (var entry in _languageNames) {
- if (entry.Value == config) {
- result.Add(entry.Key);
- }
- }
-
- return result.ToArray();
- }
-
- public string[] GetLanguageNames() {
- return ArrayUtils.MakeArray<string>(_languageNames.Keys);
- }
-
- public string[] GetFileExtensions(LanguageContext context) {
- var result = new List<string>();
- foreach (var entry in _languageExtensions) {
- if (entry.Value.LanguageContext == context) {
- result.Add(entry.Key);
- }
- }
-
- return result.ToArray();
- }
-
- internal string[] GetFileExtensions(LanguageConfiguration config) {
- var result = new List<string>();
- foreach (var entry in _languageExtensions) {
- if (entry.Value == config) {
- result.Add(entry.Key);
- }
- }
-
- return result.ToArray();
- }
-
- public string[] GetFileExtensions() {
- return ArrayUtils.MakeArray<string>(_languageExtensions.Keys);
- }
-
- internal LanguageConfiguration GetLanguageConfig(LanguageContext context) {
- foreach (var config in _languageConfigurations.Values) {
- if (config.LanguageContext == context) {
- return config;
- }
- }
- return null;
- }
- }
- }