/Languages/Ruby/Ruby/Builtins/RubyModule.cs
C# | 2244 lines | 1529 code | 407 blank | 308 comment | 365 complexity | 254d2dbc3377bdcdbff4ea9b59a02e1d MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Threading;
- using IronRuby.Compiler;
- using IronRuby.Compiler.Generation;
- using IronRuby.Runtime;
- using IronRuby.Runtime.Calls;
- using Microsoft.Scripting;
- using Microsoft.Scripting.Actions;
- using Microsoft.Scripting.Utils;
- using Microsoft.Scripting.Runtime;
- namespace IronRuby.Builtins {
- [Flags]
- public enum ModuleRestrictions {
- None = 0,
- /// <summary>
- /// Module doesn't allow its methods to be overridden.
- /// Used for built-ins, except for Object.
- /// </summary>
- NoOverrides = 1,
- /// <summary>
- /// Module doesn't allow its methods to be called by mangled (FooBar -> foo_bar) or mapped ([] -> get_Item) names.
- /// Used for built-ins.
- /// </summary>
- NoNameMapping = 2,
- /// <summary>
- /// Module is not published in the runtime global scope.
- /// </summary>
- NotPublished = 4,
- /// <summary>
- /// Module doesn't expose the underlying CLR type. The behavior is the same as if it was written in Ruby.
- /// (clr_new and clr_ctor won't work on such class/module, CLR methods won't be visible, etc.).
- /// </summary>
- NoUnderlyingType = 8,
- /// <summary>
- /// By default a non-builtin library load fails if it defines a class whose name conflicts with an existing constant name.
- /// If this restriction is applied to a class it can reopen an existing Ruby class of the same name but it can't specify an underlying type.
- /// This is required so that the library load doesn't depend on whether or not any instances of the existing Ruby class already exist.
- /// </summary>
- AllowReopening = 16 | NoUnderlyingType,
- /// <summary>
- /// Default restrictions for built-in modules.
- /// </summary>
- Builtin = NoOverrides | NoNameMapping | NotPublished,
- All = Builtin
- }
- [Flags]
- public enum MethodLookup {
- Default = 0,
- Virtual = 1,
- ReturnForwarder = 2,
- FallbackToObject = 4,
- }
- #if DEBUG
- [DebuggerDisplay("{DebugName}")]
- #endif
- [DebuggerTypeProxy(typeof(RubyModule.DebugView))]
- [ReflectionCached]
- public partial class RubyModule : IDuplicable, IRubyObject {
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
- public static readonly RubyModule[]/*!*/ EmptyArray = new RubyModule[0];
- // interlocked
- internal static int _globalMethodVersion = 0;
- internal static int _globalModuleId = 0;
- private enum State {
- Uninitialized,
- Initializing,
- Initialized
- }
- private readonly RubyContext/*!*/ _context;
-
- #region CLR Types and Namespaces
- // the namespace this module represents or null:
- private readonly NamespaceTracker _namespaceTracker;
- // The type this module represents or null.
- // TODO: unify with _underlyingSystemType on classes
- private readonly TypeTracker _typeTracker;
- public TypeTracker TypeTracker {
- get { return _typeTracker; }
- }
- public NamespaceTracker NamespaceTracker {
- get { return _namespaceTracker; }
- }
- public bool IsInterface {
- get { return _typeTracker != null && _typeTracker.Type.IsInterface; }
- }
- public bool IsClrModule {
- get { return _typeTracker != null && IsModuleType(_typeTracker.Type); }
- }
- public virtual Type/*!*/ GetUnderlyingSystemType() {
- if (IsClrModule) {
- return _typeTracker.Type;
- } else {
- throw new InvalidOperationException();
- }
- }
-
- #endregion
- private readonly ModuleRestrictions _restrictions;
- private readonly WeakReference/*!*/ _weakSelf;
-
- // name of the module or null for anonymous modules:
- private string _name;
- // Lazy interlocked init'd.
- private RubyInstanceData _instanceData;
-
- #region Immediate/Singleton/Super Class
- // Lazy interlocked init'd.
- // Classes
- // Null immediately after construction, initialized by RubyContext.CreateClass factory to a singleton class.
- // This lazy initialization is needed to allow circular references among Kernel, Object, Module and Class.
- // We create the singleton class eagerly in the factory since classes' singleton classes form a hierarchy parallel
- // to the main inheritance hierarachy of classes. If we didn't we would need to update super-references of all singleton subclasses
- // that were created after a singleton class is lazily created.
- // Modules
- // Initialized to the class of the module and may later be changed to a singleton class (only if the singleton is needed).
- // We don't create the singleton class eagerly to optimize rule generation. If we did, each (meta)module instance would receive its own immediate class
- // different from other instances of the (meta)module. And thus the instances would use different method table versions eventhough they are the same.
- // Singleton Classes
- // Self reference for dummy singletons (tha last singletons in the singleton chain).
- private RubyClass _immediateClass;
- /// <summary>
- /// A dummy singleton class is an immutable class that has no members and is used to terminate singleton class chain.
- /// </summary>
- /// <remarks>
- /// A method invoked on the last class in the singleton class chain before the dummy singleton is searched in the inheritance hierarchy of the dummy singleton:
- /// [dummy singleton, singleton(Class), singleton(Module), singleton(Object), Class, Module, Object].
- /// The ImmediateClass reference cannot be null (that would cause null-ref exception in rules), so we need to set it to the dummy.
- /// </remarks>
- public bool IsDummySingletonClass {
- get { return _immediateClass == this; }
- }
- public virtual bool IsSingletonClass {
- get { return false; }
- }
- public virtual bool IsClass {
- get { return false; }
- }
- public bool IsObjectClass {
- get { return ReferenceEquals(this, Context.ObjectClass); }
- }
- public bool IsBasicObjectClass {
- get { return ReferenceEquals(this, Context.BasicObjectClass); }
- }
- public bool IsComClass {
- get { return ReferenceEquals(this, Context.ComObjectClass); }
- }
- internal virtual RubyClass GetSuperClass() {
- return null;
- }
- // thread safe:
- internal void InitializeImmediateClass(RubyClass/*!*/ cls) {
- Debug.Assert(_immediateClass == null);
- _immediateClass = cls;
- }
- // thread safe:
- internal void InitializeImmediateClass(RubyClass/*!*/ singletonSuperClass, Action<RubyModule> trait) {
- Assert.NotNull(singletonSuperClass);
- RubyClass immediate;
- if (IsClass) {
- // class: eager singleton class construction:
- immediate = CreateSingletonClass(singletonSuperClass, trait);
- immediate.InitializeImmediateClass(_context.ClassClass.GetDummySingletonClass());
- } else if (trait != null) {
- // module: eager singleton class construction:
- immediate = CreateSingletonClass(singletonSuperClass, trait);
- immediate.InitializeImmediateClass(singletonSuperClass.GetDummySingletonClass());
- } else {
- // module: lazy singleton class construction:
- immediate = singletonSuperClass;
- }
- InitializeImmediateClass(immediate);
- }
- #endregion
- #region Mutable state guarded by ClassHierarchyLock
- [Emitted]
- public readonly VersionHandle Version;
- public readonly int Id;
- // List of dependent classes - subclasses of this class and classes to which this module is included to (forms a DAG).
- private WeakList<RubyClass> _dependentClasses;
- #if DEBUG
- private int _referringMethodRulesSinceLastUpdate;
-
- public string DebugName {
- get {
- string name;
- if (IsSingletonClass) {
- object s = ((RubyClass)this).SingletonClassOf;
- RubyModule m = s as RubyModule;
- if (m != null) {
- name = m.DebugName;
- } else {
- name = RuntimeHelpers.GetHashCode(s).ToString("x");
- }
- name = "S(" + name + ")";
- } else {
- name = _name;
- }
- return name + " #" + Id;
- }
- }
- #endif
- private enum MemberTableState {
- Uninitialized = 0,
- Initializing = 1,
- Initialized = 2
- }
- // constant table:
- private MemberTableState _constantsState = MemberTableState.Uninitialized;
- private Action<RubyModule> _constantsInitializer;
- private Dictionary<string, ConstantStorage> _constants;
-
- // method table:
- private MemberTableState _methodsState = MemberTableState.Uninitialized;
- private Dictionary<string, RubyMemberInfo> _methods;
- private Action<RubyModule> _methodsInitializer;
- // class variable table:
- private Dictionary<string, object> _classVariables;
- //
- // The entire list of modules included in this one. Newly-added mixins are at the front of the array.
- // When adding a module that itself contains other modules, Ruby tries to maintain the ordering of the
- // contained modules so that method resolution is reasonably consistent.
- //
- // MRO walk: this, _mixins[0], _mixins[1], ..., _mixins[n-1], super, ...
- private RubyModule[]/*!*/ _mixins;
- // A list of extension methods included into this type or null if none were included.
- // { method-name -> methods }
- internal Dictionary<string, List<ExtensionMethodInfo>> _extensionMethods;
-
- #endregion
- #region Dynamic Sites
- // RubyModule, symbol -> object
- private CallSite<Func<CallSite, object, object, object>> _constantMissingCallbackSite;
- private CallSite<Func<CallSite, object, object, object>> _methodAddedCallbackSite;
- private CallSite<Func<CallSite, object, object, object>> _methodRemovedCallbackSite;
- private CallSite<Func<CallSite, object, object, object>> _methodUndefinedCallbackSite;
- internal object ConstantMissing(string/*!*/ name) {
- return Context.Send(ref _constantMissingCallbackSite, "const_missing", this, name);
- }
- // Ruby 1.8: called after method is added, except for alias_method which calls it before
- // Ruby 1.9: called before method is added
- public virtual void MethodAdded(string/*!*/ name) {
- Assert.NotNull(name);
- Debug.Assert(!IsSingletonClass);
- Context.Send(ref _methodAddedCallbackSite, Symbols.MethodAdded, this, name);
- }
- internal virtual void MethodRemoved(string/*!*/ name) {
- Assert.NotNull(name);
- Debug.Assert(!IsSingletonClass);
- Context.Send(ref _methodRemovedCallbackSite, Symbols.MethodRemoved, this, name);
- }
- internal virtual void MethodUndefined(string/*!*/ name) {
- Assert.NotNull(name);
- Debug.Assert(!IsSingletonClass);
- Context.Send(ref _methodUndefinedCallbackSite, Symbols.MethodUndefined, this, name);
- }
- #endregion
- public ModuleRestrictions Restrictions {
- get { return _restrictions; }
- }
- internal RubyModule[]/*!*/ Mixins {
- get { return _mixins; }
- }
- public string Name {
- get { return _name; }
- internal set { _name = value; }
- }
- public RubyContext/*!*/ Context {
- get { return _context; }
- }
- internal virtual RubyGlobalScope GlobalScope {
- get { return null; }
- }
- internal WeakReference/*!*/ WeakSelf {
- get { return _weakSelf; }
- }
- internal Dictionary<string, List<ExtensionMethodInfo>> ExtensionMethods {
- get { return _extensionMethods; }
- }
- // default allocator:
- public RubyModule(RubyClass/*!*/ metaModuleClass)
- : this(metaModuleClass, null) {
- }
- // creates an empty (meta)module:
- protected RubyModule(RubyClass/*!*/ metaModuleClass, string name)
- : this(metaModuleClass.Context, name, null, null, null, null, null, ModuleRestrictions.None) {
-
- // metaModuleClass represents a subclass of Module or its duplicate (Kernel#dup)
- InitializeImmediateClass(metaModuleClass, null);
- }
- internal RubyModule(RubyContext/*!*/ context, string name, Action<RubyModule> methodsInitializer, Action<RubyModule> constantsInitializer,
- RubyModule/*!*/[] expandedMixins, NamespaceTracker namespaceTracker, TypeTracker typeTracker, ModuleRestrictions restrictions) {
- Assert.NotNull(context);
- Debug.Assert(namespaceTracker == null || typeTracker == null || typeTracker.Type == typeof(object));
- Debug.Assert(expandedMixins == null ||
- CollectionUtils.TrueForAll(expandedMixins, (m) => m != this && m != null && !m.IsClass && m.Context == context)
- );
- _context = context;
- _name = name;
- _methodsInitializer = methodsInitializer;
- _constantsInitializer = constantsInitializer;
- _namespaceTracker = namespaceTracker;
- _typeTracker = typeTracker;
- _mixins = expandedMixins ?? EmptyArray;
- _restrictions = restrictions;
- _weakSelf = new WeakReference(this);
- Version = new VersionHandle(Interlocked.Increment(ref _globalMethodVersion));
- Version.SetName(name);
- Id = Interlocked.Increment(ref _globalModuleId);
- }
- #region Initialization (thread-safe)
- internal bool ConstantInitializationNeeded {
- get { return _constantsState == MemberTableState.Uninitialized; }
- }
- private void InitializeConstantTableNoLock() {
- if (!ConstantInitializationNeeded) return;
- _constants = new Dictionary<string, ConstantStorage>();
- _constantsState = MemberTableState.Initializing;
- try {
- if (_constantsInitializer != EmptyInitializer) {
- if (_constantsInitializer != null) {
- Utils.Log(_name ?? "<anonymous>", "CT_INIT");
- // TODO: use lock-free operations in initializers
- _constantsInitializer(this);
- } else if (_typeTracker != null && !_typeTracker.Type.IsInterface) {
- // Load types eagerly. We do this only for CLR types that have no constant initializer (not builtins) and
- // a constant access is performed (otherwise this method wouldn't be called).
- //
- // Note: Interfaces cannot declare nested types in C#, we follow the suit here.
- // We don't currently need this restriction but once we implement generic type overload inheritance properly
- // we would need to deal with inheritance from interfaces, which might be too complex.
- //
- LoadNestedTypes();
- }
- }
- } finally {
- _constantsInitializer = null;
- _constantsState = MemberTableState.Initialized;
- }
- }
- internal static readonly Action<RubyModule> EmptyInitializer = (_) => { };
- internal bool MethodInitializationNeeded {
- get { return _methodsState == MemberTableState.Uninitialized; }
- }
- private void InitializeMethodTableNoLock() {
- if (!MethodInitializationNeeded) return;
- InitializeDependencies();
- _methods = new Dictionary<string, RubyMemberInfo>();
- _methodsState = MemberTableState.Initializing;
- try {
- if (_methodsInitializer != null) {
- Utils.Log(_name ?? "<anonymous>", "MT_INIT");
- // TODO: use lock-free operations in initializers?
- _methodsInitializer(this);
- }
- } finally {
- _methodsInitializer = null;
- _methodsState = MemberTableState.Initialized;
- }
- }
- internal void InitializeMethodsNoLock() {
- if (MethodInitializationNeeded) {
- InitializeMethodsNoLock(GetUninitializedAncestors(true));
- }
- }
- internal void InitializeMethodsNoLock(IList<RubyModule/*!*/>/*!*/ modules) {
- for (int i = modules.Count - 1; i >= 0; i--) {
- modules[i].InitializeMethodTableNoLock();
- }
- }
- internal void InitializeConstantsNoLock() {
- if (ConstantInitializationNeeded) {
- InitializeConstantsNoLock(GetUninitializedAncestors(false));
- }
- }
- internal void InitializeConstantsNoLock(IList<RubyModule/*!*/>/*!*/ modules) {
- for (int i = modules.Count - 1; i >= 0; i--) {
- modules[i].InitializeConstantTableNoLock();
- }
- }
- private List<RubyModule>/*!*/ GetUninitializedAncestors(bool methods) {
- var result = new List<RubyModule>();
- result.Add(this);
- result.AddRange(_mixins);
- var super = GetSuperClass();
- while (super != null && (methods ? super.MethodInitializationNeeded : super.ConstantInitializationNeeded)) {
- result.Add(super);
- result.AddRange(super._mixins);
- super = super.SuperClass;
- }
- return result;
- }
- private void InitializeClassVariableTable() {
- if (_classVariables == null) {
- Interlocked.CompareExchange(ref _classVariables, new Dictionary<string, object>(), null);
- }
- }
- private void LoadNestedTypes() {
- Context.RequiresClassHierarchyLock();
- Debug.Assert(_constants != null && _constants.Count == 0);
- // TODO: Inherited generic overloads. We need a custom TypeGroup to do it right - part of the type group might be removed
- // TODO: protected types
- var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly;
- if (Context.DomainManager.Configuration.PrivateBinding) {
- bindingFlags |= BindingFlags.NonPublic;
- }
- // if the constant is redefined/removed from the base class. This is similar to method overload inheritance.
- Type[] types = _typeTracker.Type.GetNestedTypes(bindingFlags);
- var trackers = new List<TypeTracker>();
- var names = new List<string>();
- foreach (var type in types) {
- TypeTracker tracker = (NestedTypeTracker)MemberTracker.FromMemberInfo(type);
- var name = (type.IsGenericType) ? ReflectionUtils.GetNormalizedTypeName(type) : type.Name;
- int index = names.IndexOf(name);
- if (index != -1) {
- trackers[index] = TypeGroup.UpdateTypeEntity(trackers[index], tracker);
- names[index] = name;
- } else {
- trackers.Add(tracker);
- names.Add(name);
- }
- }
- for (int i = 0; i < trackers.Count; i++) {
- var tracker = trackers[i];
- ConstantStorage storage;
- if (tracker is TypeGroup) {
- storage = new ConstantStorage(tracker, new WeakReference(tracker));
- } else {
- var module = Context.GetModule(tracker.Type);
- storage = new ConstantStorage(module, module.WeakSelf);
- }
- _constants[names[i]] = storage;
- }
- }
- internal void InitializeMembersFrom(RubyModule/*!*/ module) {
- Context.RequiresClassHierarchyLock();
- Mutate();
- Assert.NotNull(module);
- if (module._namespaceTracker != null && _constants == null) {
- // initialize the module so that we can copy all constants from it:
- module.InitializeConstantsNoLock();
- // initialize all ancestors of self:
- InitializeConstantsNoLock();
- } else {
- _constantsInitializer = Utils.CloneInvocationChain(module._constantsInitializer);
- _constantsState = module._constantsState;
- }
- _constants = (module._constants != null) ? new Dictionary<string, ConstantStorage>(module._constants) : null;
- // copy namespace members:
- if (module._namespaceTracker != null) {
- Debug.Assert(_constants != null);
- foreach (KeyValuePair<string, object> constant in module._namespaceTracker) {
- _constants.Add(constant.Key, new ConstantStorage(constant.Value));
- }
- }
- _methodsInitializer = Utils.CloneInvocationChain(module._methodsInitializer);
- _methodsState = module._methodsState;
- if (module._methods != null) {
- _methods = new Dictionary<string, RubyMemberInfo>(module._methods.Count);
- foreach (var method in module._methods) {
- _methods[method.Key] = method.Value.Copy(method.Value.Flags, this);
- }
- } else {
- _methods = null;
- }
- _classVariables = (module._classVariables != null) ? new Dictionary<string, object>(module._classVariables) : null;
- _mixins = ArrayUtils.Copy(module._mixins);
- // dependentModules - skip
- // tracker - skip, .NET members not copied
-
- // TODO:
- // - handle overloads cached in groups
- // - version updates
- MethodsUpdated("InitializeFrom");
- }
- public void InitializeModuleCopy(RubyModule/*!*/ module) {
- if (_context.IsObjectFrozen(this)) {
- throw RubyExceptions.CreateTypeError("can't modify frozen Module");
- }
- using (Context.ClassHierarchyLocker()) {
- InitializeMembersFrom(module);
- }
- }
- object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) {
-
- // capture the current immediate class (it can change any time if it not a singleton class)
- RubyClass immediate = _immediateClass;
- RubyModule result = new RubyModule(immediate.IsSingletonClass ? immediate.SuperClass : immediate, null);
- // singleton members are copied here, not in InitializeCopy:
- if (copySingletonMembers && immediate.IsSingletonClass) {
- var singletonClass = result.GetOrCreateSingletonClass();
- using (Context.ClassHierarchyLocker()) {
- singletonClass.InitializeMembersFrom(immediate);
- }
- }
-
- // copy instance variables:
- _context.CopyInstanceData(this, result, false);
- return result;
- }
- #endregion
- #region Versioning (thread-safe)
- // A version of a frozen module can still change if its super-classes/mixins change.
- private void Mutate() {
- Debug.Assert(!IsDummySingletonClass);
- if (IsFrozen) {
- throw RubyExceptions.CreateRuntimeError(String.Format("can't modify frozen {0}", IsClass ? "class" : "module"));
- }
- }
- [Conditional("DEBUG")]
- internal void OwnedMethodCachedInSite() {
- Context.RequiresClassHierarchyLock();
- #if DEBUG
- _referringMethodRulesSinceLastUpdate++;
- #endif
- }
- internal virtual void InitializeDependencies() {
- // nop
- }
- internal WeakList<RubyClass/*!*/>/*!*/ DependentClasses {
- get {
- Context.RequiresClassHierarchyLock();
- if (_dependentClasses == null) {
- _dependentClasses = new WeakList<RubyClass>();
- }
- return _dependentClasses;
- }
- }
- internal void AddDependentClass(RubyClass/*!*/ dependentClass) {
- Context.RequiresClassHierarchyLock();
- Assert.NotNull(dependentClass);
- foreach (var cls in DependentClasses) {
- if (ReferenceEquals(dependentClass, cls)) {
- return;
- }
- }
- DependentClasses.Add(dependentClass.WeakSelf);
- }
- private void IncrementMethodVersion() {
- if (IsClass) {
- Version.Method = Interlocked.Increment(ref _globalMethodVersion);
- }
- }
- internal void MethodsUpdated(string/*!*/ reason) {
- Context.RequiresClassHierarchyLock();
- int affectedModules = 0;
- int affectedRules = 0;
- Func<RubyModule, bool> visitor = (module) => {
- module.IncrementMethodVersion();
- #if DEBUG
- affectedModules++;
- affectedRules += module._referringMethodRulesSinceLastUpdate;
- module._referringMethodRulesSinceLastUpdate = 0;
- #endif
- // TODO (opt?): stop updating if a class that defines a method of the same name is reached.
- return false;
- };
- visitor(this);
- ForEachRecursivelyDependentClass(visitor);
- Utils.Log(String.Format("{0,-50} {1,-30} affected={2,-5} rules={3,-5}", Name, reason, affectedModules, affectedRules), "UPDATED");
- }
- /// <summary>
- /// Calls given action on all modules that are directly or indirectly nested into this module.
- /// </summary>
- private bool ForEachRecursivelyDependentClass(Func<RubyModule, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- if (_dependentClasses != null) {
- foreach (var cls in _dependentClasses) {
- if (action(cls)) {
- return true;
- }
- if (cls.ForEachRecursivelyDependentClass(action)) {
- return true;
- }
- }
- }
- return false;
- }
- #endregion
- #region IRubyObject Members
- // thread-safe:
- public RubyClass ImmediateClass {
- get {
- return _immediateClass;
- }
- set {
- throw new InvalidOperationException("Cannot change the immediate class of a module");
- }
- }
- // thread-safe:
- public RubyInstanceData TryGetInstanceData() {
- return _instanceData;
- }
- // thread-safe:
- public RubyInstanceData GetInstanceData() {
- return RubyOps.GetInstanceData(ref _instanceData);
- }
- // thread-safe:
- public virtual bool IsFrozen {
- get { return IsModuleFrozen; }
- }
- // thread-safe: _instanceData cannot be unset
- internal bool IsModuleFrozen {
- get { return _instanceData != null && _instanceData.IsFrozen; }
- }
- // thread-safe:
- public void Freeze() {
- GetInstanceData().Freeze();
- }
- // thread-safe:
- public bool IsTainted {
- get { return GetInstanceData().IsTainted; }
- set { GetInstanceData().IsTainted = value; }
- }
- // thread-safe:
- public bool IsUntrusted {
- get { return GetInstanceData().IsUntrusted; }
- set { GetInstanceData().IsUntrusted = value; }
- }
- int IRubyObject.BaseGetHashCode() {
- return base.GetHashCode();
- }
- bool IRubyObject.BaseEquals(object other) {
- return base.Equals(other);
- }
- string/*!*/ IRubyObject.BaseToString() {
- return base.ToString();
- }
- public override string/*!*/ ToString() {
- return _name ?? "<anonymous>";
- }
- #endregion
- #region Factories (thread-safe)
- // Ruby constructor:
- public static object CreateAnonymousModule(RubyScope/*!*/ scope, BlockParam body, RubyClass/*!*/ self) {
- RubyModule newModule = new RubyModule(self, null);
- return (body != null) ? RubyUtils.EvaluateInModule(newModule, body, null, newModule) : newModule;
- }
- // thread safe:
- public RubyClass/*!*/ GetOrCreateSingletonClass() {
- if (IsDummySingletonClass) {
- throw new InvalidOperationException("Dummy singleton class has no singleton class");
- }
- RubyClass immediate = _immediateClass;
- RubyClass singletonSuper;
- RubyClass singletonImmediate;
- if (!immediate.IsSingletonClass) {
- // finish module singleton initialization:
- Debug.Assert(!IsClass);
- singletonSuper = immediate;
- singletonImmediate = immediate.GetDummySingletonClass();
- } else if (immediate.IsDummySingletonClass) {
- // expanding singleton chain:
- singletonSuper = immediate.SuperClass;
- singletonImmediate = immediate;
- } else {
- return immediate;
- }
- var singleton = CreateSingletonClass(singletonSuper, null);
- singleton.InitializeImmediateClass(singletonImmediate);
- Interlocked.CompareExchange(ref _immediateClass, singleton, immediate);
- Debug.Assert(_immediateClass.IsSingletonClass && !_immediateClass.IsDummySingletonClass);
- return _immediateClass;
- }
- /// <summary>
- /// Create a new singleton class for this module.
- /// Doesn't attach this module to it yet, the caller needs to do so.
- /// </summary>
- /// <remarks>Thread safe.</remarks>
- internal RubyClass/*!*/ CreateSingletonClass(RubyClass/*!*/ superClass, Action<RubyModule> trait) {
- // Note that in MRI, member tables of dummy singleton are shared with the class the dummy is singleton for
- // This is obviously an implementation detail leaking to the language and we don't support that.
- // real class object and it's singleton share the tracker:
- TypeTracker tracker = (IsSingletonClass) ? null : _typeTracker;
- // Singleton should have the same restrictions as the module it is singleton for.
- // Reason: We need static methods of builtins (e.g. Object#Equals) not to be exposed under Ruby names (Object#equals).
- // We also want static methods of non-builtins to be visible under both CLR and Ruby names.
- var result = new RubyClass(
- Context, null, null, this, trait, null, null, superClass, null, tracker, null, false, true, this.Restrictions
- );
- #if DEBUG
- result.Version.SetName(result.DebugName);
- #endif
- return result;
- }
- #endregion
- #region Ancestors (thread-safe)
- // Return true from action to terminate enumeration.
- public bool ForEachAncestor(bool inherited, Func<RubyModule/*!*/, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
-
- if (inherited) {
- return ForEachAncestor(action);
- } else {
- return ForEachDeclaredAncestor(action);
- }
- }
- internal virtual bool ForEachAncestor(Func<RubyModule/*!*/, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- return ForEachDeclaredAncestor(action);
- }
- internal bool ForEachDeclaredAncestor(Func<RubyModule/*!*/, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- // this module:
- if (action(this)) return true;
- // mixins:
- foreach (RubyModule m in _mixins) {
- if (action(m)) return true;
- }
- return false;
- }
- #endregion
- #region Constants (thread-safe)
- // Value of constant that is to be auto-loaded on first use.
- private sealed class AutoloadedConstant {
- private readonly MutableString/*!*/ _path;
- private bool _loaded;
- // File already loaded? An auto-loaded constant can be referenced by mutliple classes (via duplication).
- // After the constant is accessed in one of them and the file is loaded access to the other duplicates doesn't trigger file load.
- public bool Loaded { get { return _loaded; } }
- public MutableString/*!*/ Path { get { return _path; } }
- public AutoloadedConstant(MutableString/*!*/ path) {
- Assert.NotNull(path);
- Debug.Assert(path.IsFrozen);
- _path = path;
- }
- public bool Load(RubyGlobalScope/*!*/ autoloadScope) {
- if (_loaded) {
- return false;
- }
- using (autoloadScope.Context.ClassHierarchyUnlocker()) {
- _loaded = true;
- return autoloadScope.Context.Loader.LoadFile(autoloadScope.Scope, null, _path, LoadFlags.Require);
- }
- }
- }
- public string/*!*/ MakeNestedModuleName(string nestedModuleSimpleName) {
- return (IsObjectClass || nestedModuleSimpleName == null) ?
- nestedModuleSimpleName :
- _name + "::" + nestedModuleSimpleName;
- }
- // not thread-safe
- public void ForEachConstant(bool inherited, Func<RubyModule/*!*/, string/*!*/, object, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) {
- // notification that we entered the module (it could have no constant):
- if (action(module, null, Missing.Value)) return true;
- return module.EnumerateConstants(action);
- });
- }
- // thread-safe:
- public void SetConstant(string/*!*/ name, object value) {
- using (Context.ClassHierarchyLocker()) {
- SetConstantNoLock(name, value);
- }
- }
- internal void Publish(string/*!*/ name) {
- RubyOps.ScopeSetMember(_context.TopGlobalScope, name, this);
- }
- private void SetConstantNoLock(string/*!*/ name, object value) {
- Mutate();
- SetConstantNoMutateNoLock(name, value);
- }
- internal void SetConstantNoMutateNoLock(string/*!*/ name, object value) {
- Context.RequiresClassHierarchyLock();
- InitializeConstantsNoLock();
- _context.ConstantAccessVersion++;
- _constants[name] = new ConstantStorage(value);
- }
- /// <summary>
- /// Sets constant of this module.
- /// Returns true if the constant is already defined in the module and it is not an autoloaded constant.
- /// </summary>
- /// <remarks>
- /// Thread safe.
- /// </remarks>
- public bool SetConstantChecked(string/*!*/ name, object value) {
- using (Context.ClassHierarchyLocker()) {
- ConstantStorage existing;
- var result = TryLookupConstantNoLock(false, false, null, name, out existing);
- SetConstantNoLock(name, value);
- return result == ConstantLookupResult.Found;
- }
- }
-
- // thread-safe:
- public void SetAutoloadedConstant(string/*!*/ name, MutableString/*!*/ path) {
- ConstantStorage dummy;
- if (!TryGetConstant(null, name, out dummy)) {
- SetConstant(name, new AutoloadedConstant(MutableString.Create(path).Freeze()));
- }
- }
- // thread-safe:
- public MutableString GetAutoloadedConstantPath(string/*!*/ name) {
- using (Context.ClassHierarchyLocker()) {
- ConstantStorage storage;
- AutoloadedConstant autoloaded;
- return (TryGetConstantNoAutoloadCheck(name, out storage)
- && (autoloaded = storage.Value as AutoloadedConstant) != null
- && !autoloaded.Loaded) ?
- autoloaded.Path : null;
- }
- }
- internal bool TryResolveConstant(RubyContext/*!*/ callerContext, RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
- return callerContext != Context ?
- TryResolveConstant(autoloadScope, name, out value) :
- TryResolveConstantNoLock(autoloadScope, name, out value);
- }
- public bool TryGetConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out object value) {
- ConstantStorage storage;
- var result = TryGetConstant(autoloadScope, name, out storage);
- value = storage.Value;
- return result;
- }
- /// <summary>
- /// Get constant defined in this module.
- /// </summary>
- internal bool TryGetConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
- using (Context.ClassHierarchyLocker()) {
- return TryGetConstantNoLock(autoloadScope, name, out value);
- }
- }
- /// <summary>
- /// Get constant defined in this module.
- /// </summary>
- internal bool TryGetConstantNoLock(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
- Context.RequiresClassHierarchyLock();
- return TryLookupConstantNoLock(false, false, autoloadScope, name, out value) != ConstantLookupResult.NotFound;
- }
- /// <summary>
- /// Get constant defined in this module or any of its ancestors.
- /// Autoloads if autoloadScope is not null.
- /// </summary>
- /// <remarks>
- /// Thread safe.
- /// </remarks>
- internal bool TryResolveConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
- using (Context.ClassHierarchyLocker()) {
- return TryResolveConstantNoLock(autoloadScope, name, out value);
- }
- }
- /// <summary>
- /// Get constant defined in this module or any of its ancestors.
- /// </summary>
- internal bool TryResolveConstantNoLock(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
- Context.RequiresClassHierarchyLock();
- return TryLookupConstantNoLock(true, true, autoloadScope, name, out value) != ConstantLookupResult.NotFound;
- }
- private enum ConstantLookupResult {
- NotFound = 0,
- Found = 1,
- FoundAutoload = 2,
- }
- private ConstantLookupResult TryLookupConstantNoLock(bool included, bool inherited, RubyGlobalScope autoloadScope,
- string/*!*/ name, out ConstantStorage value) {
- Context.RequiresClassHierarchyLock();
- Debug.Assert(included || !inherited);
- value = default(ConstantStorage);
- while (true) {
- ConstantStorage result;
- RubyModule owner = included ?
- TryResolveConstantNoAutoloadCheck(inherited, name, out result) :
- (TryGetConstantNoAutoloadCheck(name, out result) ? this : null);
- if (owner == null) {
- return ConstantLookupResult.NotFound;
- }
- var autoloaded = result.Value as AutoloadedConstant;
- if (autoloaded == null) {
- value = result;
- return ConstantLookupResult.Found;
- }
- if (autoloadScope == null) {
- return ConstantLookupResult.FoundAutoload;
- }
- if (autoloadScope.Context != Context) {
- throw RubyExceptions.CreateTypeError(String.Format("Cannot autoload constants to a foreign runtime #{0}", autoloadScope.Context.RuntimeId));
- }
- // autoloaded constants are removed before the associated file is loaded:
- object _;
- owner.TryRemoveConstantNoLock(name, out _);
-
- // load file and try lookup again (releases the class hierarchy lock when loading the file):
- if (!autoloaded.Load(autoloadScope)) {
- return ConstantLookupResult.NotFound;
- }
- }
- }
- // Returns the owner of the constant or null if the constant is not found.
- private RubyModule TryResolveConstantNoAutoloadCheck(bool inherited, string/*!*/ name, out ConstantStorage value) {
- Context.RequiresClassHierarchyLock();
- var storage = default(ConstantStorage);
- RubyModule owner = null;
- if (ForEachAncestor(inherited, (module) => (owner = module).TryGetConstantNoAutoloadCheck(name, out storage))) {
- value = storage;
- return owner;
- } else {
- value = storage;
- return null;
- }
- }
- // Returns the owner of the constant (this module) or null if the constant is not found.
- internal bool TryGetConstantNoAutoloadCheck(string/*!*/ name, out ConstantStorage storage) {
- Context.RequiresClassHierarchyLock();
- if (name.Length == 0) {
- storage = default(ConstantStorage);
- return false;
- }
- InitializeConstantsNoLock();
- if (_constants.TryGetValue(name, out storage)) {
- if (storage.IsRemoved) {
- storage = default(ConstantStorage);
- return false;
- } else {
- return true;
- }
- }
- if (_namespaceTracker != null) {
- object value;
- if (_namespaceTracker.TryGetValue(name, out value)) {
- storage = new ConstantStorage( _context.TrackerToModule(value));
- return true;
- }
- }
- storage = default(ConstantStorage);
- return false;
- }
- // Direct lookup into the constant table (if it exists).
- internal bool TryGetConstantNoAutoloadNoInit(string/*!*/ name, out ConstantStorage storage) {
- Context.RequiresClassHierarchyLock();
- storage = default(ConstantStorage);
- return _constants != null && _constants.TryGetValue(name, out storage) && !storage.IsRemoved;
- }
- // thread-safe:
- public bool TryRemoveConstant(string/*!*/ name, out object value) {
- using (Context.ClassHierarchyLocker()) {
- return TryRemoveConstantNoLock(name, out value);
- }
- }
- private bool TryRemoveConstantNoLock(string/*!*/ name, out object value) {
- Context.RequiresClassHierarchyLock();
- InitializeConstantsNoLock();
- bool result;
- ConstantStorage storage;
- if (_constants.TryGetValue(name, out storage)) {
- if (storage.IsRemoved) {
- value = null;
- return false;
- } else {
- value = storage.Value;
- result = true;
- }
- } else {
- value = null;
- result = false;
- }
- object namespaceValue;
- if (_namespaceTracker != null && _namespaceTracker.TryGetValue(name, out namespaceValue)) {
- _constants[name] = ConstantStorage.Removed;
- _context.ConstantAccessVersion++;
- value = namespaceValue;
- result = true;
- } else if (result) {
- _constants.Remove(name);
- _context.ConstantAccessVersion++;
- }
- return result;
- }
- public bool EnumerateConstants(Func<RubyModule, string, object, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- InitializeConstantsNoLock();
- foreach (var constant in _constants) {
- var name = constant.Key;
- var storage = constant.Value;
- if (!storage.IsRemoved && action(this, name, storage.Value)) {
- return true;
- }
- }
- if (_namespaceTracker != null) {
- foreach (KeyValuePair<string, object> constant in _namespaceTracker) {
- string name = constant.Key;
- // we check if we haven't already yielded the value so that we don't yield values hidden by a user defined constant:
- if (!_constants.ContainsKey(name) && action(this, name, constant.Value)) {
- return true;
- }
- }
- }
- return false;
- }
- #endregion
- #region Methods (thread-safe)
- // not thread-safe:
- public void ForEachInstanceMethod(bool inherited, Func<RubyModule/*!*/, string/*!*/, RubyMemberInfo, bool>/*!*/ action) {
- Context.RequiresClassHierarchyLock();
- ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) {
- // Skip CLR modules (methods declared on CLR modules have already been looked for in the class).
- // If 'this' is a CLR module, we want to visit all mixed-in methods.
- if (module.IsClrModule && !this.IsClrModule) return false;
- // notification that we entered the module (it could have no method):
- if (action(module, null, null)) return true;
- return module.EnumerateMethods(action);
- });
- }
- // thread-safe:
- public void AddMethodAlias(string/*!*/ newName, string/*!*/ oldName) {
- // MRI 1.8: if (newName == oldName) return;
- // MRI 1.9: no check
- RubyMemberInfo method;
- using (Context.ClassHierarchyLocker()) {
- // MRI: aliases a super-forwarder not the real method.
- method = ResolveMethodNoLock(oldName, VisibilityContext.AllVisible, MethodLookup.FallbackToObject | MethodLookup.ReturnForwarder).Info;
- if (method == null) {
- throw RubyExceptions.CreateUndefinedMethodError(this, oldName);
- }
- // Alias preserves visibility and declaring module even though the alias is declared in a different module (e.g. subclass) =>
- // we can share method info (in fact, sharing is sound with Method#== semantics - it returns true on aliased methods).
- //
- // CLR members:
- // Detaches the member from its underlying type (by creating a copy).
- // Note: We need to copy overload group since otherwise it might mess up caching if the alias is defined in a sub-module and
- // overloads of the same name that are not included in the overload group are inherited to this module.
- // EnumerateMethods also relies on overload groups only representing cached CLR members.
- if (!method.IsRubyMember) {
- SetMethodNoEventNoLock(Context, newName, method.Copy(method.Flags, method.DeclaringModule));
- } else {
- SetMethodNoEventNoLock(Context, newName, method);
- }
- }
- MethodAdded(newName);
- }
- // Module#define_method:
- public void SetDefinedMethodNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method, RubyMethodVisibility visibility) {
- // CLR members: Detaches the member from its underlying type (by creat…
Large files files are truncated, but you can click here to view the full file