PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Languages/Ruby/Ruby/Builtins/RubyModule.cs

http://github.com/IronLanguages/main
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
  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. * ironruby@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. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Reflection;
  19. using System.Runtime.CompilerServices;
  20. using System.Threading;
  21. using IronRuby.Compiler;
  22. using IronRuby.Compiler.Generation;
  23. using IronRuby.Runtime;
  24. using IronRuby.Runtime.Calls;
  25. using Microsoft.Scripting;
  26. using Microsoft.Scripting.Actions;
  27. using Microsoft.Scripting.Utils;
  28. using Microsoft.Scripting.Runtime;
  29. namespace IronRuby.Builtins {
  30. [Flags]
  31. public enum ModuleRestrictions {
  32. None = 0,
  33. /// <summary>
  34. /// Module doesn't allow its methods to be overridden.
  35. /// Used for built-ins, except for Object.
  36. /// </summary>
  37. NoOverrides = 1,
  38. /// <summary>
  39. /// Module doesn't allow its methods to be called by mangled (FooBar -> foo_bar) or mapped ([] -> get_Item) names.
  40. /// Used for built-ins.
  41. /// </summary>
  42. NoNameMapping = 2,
  43. /// <summary>
  44. /// Module is not published in the runtime global scope.
  45. /// </summary>
  46. NotPublished = 4,
  47. /// <summary>
  48. /// Module doesn't expose the underlying CLR type. The behavior is the same as if it was written in Ruby.
  49. /// (clr_new and clr_ctor won't work on such class/module, CLR methods won't be visible, etc.).
  50. /// </summary>
  51. NoUnderlyingType = 8,
  52. /// <summary>
  53. /// By default a non-builtin library load fails if it defines a class whose name conflicts with an existing constant name.
  54. /// 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.
  55. /// This is required so that the library load doesn't depend on whether or not any instances of the existing Ruby class already exist.
  56. /// </summary>
  57. AllowReopening = 16 | NoUnderlyingType,
  58. /// <summary>
  59. /// Default restrictions for built-in modules.
  60. /// </summary>
  61. Builtin = NoOverrides | NoNameMapping | NotPublished,
  62. All = Builtin
  63. }
  64. [Flags]
  65. public enum MethodLookup {
  66. Default = 0,
  67. Virtual = 1,
  68. ReturnForwarder = 2,
  69. FallbackToObject = 4,
  70. }
  71. #if DEBUG
  72. [DebuggerDisplay("{DebugName}")]
  73. #endif
  74. [DebuggerTypeProxy(typeof(RubyModule.DebugView))]
  75. [ReflectionCached]
  76. public partial class RubyModule : IDuplicable, IRubyObject {
  77. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
  78. public static readonly RubyModule[]/*!*/ EmptyArray = new RubyModule[0];
  79. // interlocked
  80. internal static int _globalMethodVersion = 0;
  81. internal static int _globalModuleId = 0;
  82. private enum State {
  83. Uninitialized,
  84. Initializing,
  85. Initialized
  86. }
  87. private readonly RubyContext/*!*/ _context;
  88. #region CLR Types and Namespaces
  89. // the namespace this module represents or null:
  90. private readonly NamespaceTracker _namespaceTracker;
  91. // The type this module represents or null.
  92. // TODO: unify with _underlyingSystemType on classes
  93. private readonly TypeTracker _typeTracker;
  94. public TypeTracker TypeTracker {
  95. get { return _typeTracker; }
  96. }
  97. public NamespaceTracker NamespaceTracker {
  98. get { return _namespaceTracker; }
  99. }
  100. public bool IsInterface {
  101. get { return _typeTracker != null && _typeTracker.Type.IsInterface; }
  102. }
  103. public bool IsClrModule {
  104. get { return _typeTracker != null && IsModuleType(_typeTracker.Type); }
  105. }
  106. public virtual Type/*!*/ GetUnderlyingSystemType() {
  107. if (IsClrModule) {
  108. return _typeTracker.Type;
  109. } else {
  110. throw new InvalidOperationException();
  111. }
  112. }
  113. #endregion
  114. private readonly ModuleRestrictions _restrictions;
  115. private readonly WeakReference/*!*/ _weakSelf;
  116. // name of the module or null for anonymous modules:
  117. private string _name;
  118. // Lazy interlocked init'd.
  119. private RubyInstanceData _instanceData;
  120. #region Immediate/Singleton/Super Class
  121. // Lazy interlocked init'd.
  122. // Classes
  123. // Null immediately after construction, initialized by RubyContext.CreateClass factory to a singleton class.
  124. // This lazy initialization is needed to allow circular references among Kernel, Object, Module and Class.
  125. // We create the singleton class eagerly in the factory since classes' singleton classes form a hierarchy parallel
  126. // to the main inheritance hierarachy of classes. If we didn't we would need to update super-references of all singleton subclasses
  127. // that were created after a singleton class is lazily created.
  128. // Modules
  129. // Initialized to the class of the module and may later be changed to a singleton class (only if the singleton is needed).
  130. // 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
  131. // different from other instances of the (meta)module. And thus the instances would use different method table versions eventhough they are the same.
  132. // Singleton Classes
  133. // Self reference for dummy singletons (tha last singletons in the singleton chain).
  134. private RubyClass _immediateClass;
  135. /// <summary>
  136. /// A dummy singleton class is an immutable class that has no members and is used to terminate singleton class chain.
  137. /// </summary>
  138. /// <remarks>
  139. /// 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:
  140. /// [dummy singleton, singleton(Class), singleton(Module), singleton(Object), Class, Module, Object].
  141. /// The ImmediateClass reference cannot be null (that would cause null-ref exception in rules), so we need to set it to the dummy.
  142. /// </remarks>
  143. public bool IsDummySingletonClass {
  144. get { return _immediateClass == this; }
  145. }
  146. public virtual bool IsSingletonClass {
  147. get { return false; }
  148. }
  149. public virtual bool IsClass {
  150. get { return false; }
  151. }
  152. public bool IsObjectClass {
  153. get { return ReferenceEquals(this, Context.ObjectClass); }
  154. }
  155. public bool IsBasicObjectClass {
  156. get { return ReferenceEquals(this, Context.BasicObjectClass); }
  157. }
  158. public bool IsComClass {
  159. get { return ReferenceEquals(this, Context.ComObjectClass); }
  160. }
  161. internal virtual RubyClass GetSuperClass() {
  162. return null;
  163. }
  164. // thread safe:
  165. internal void InitializeImmediateClass(RubyClass/*!*/ cls) {
  166. Debug.Assert(_immediateClass == null);
  167. _immediateClass = cls;
  168. }
  169. // thread safe:
  170. internal void InitializeImmediateClass(RubyClass/*!*/ singletonSuperClass, Action<RubyModule> trait) {
  171. Assert.NotNull(singletonSuperClass);
  172. RubyClass immediate;
  173. if (IsClass) {
  174. // class: eager singleton class construction:
  175. immediate = CreateSingletonClass(singletonSuperClass, trait);
  176. immediate.InitializeImmediateClass(_context.ClassClass.GetDummySingletonClass());
  177. } else if (trait != null) {
  178. // module: eager singleton class construction:
  179. immediate = CreateSingletonClass(singletonSuperClass, trait);
  180. immediate.InitializeImmediateClass(singletonSuperClass.GetDummySingletonClass());
  181. } else {
  182. // module: lazy singleton class construction:
  183. immediate = singletonSuperClass;
  184. }
  185. InitializeImmediateClass(immediate);
  186. }
  187. #endregion
  188. #region Mutable state guarded by ClassHierarchyLock
  189. [Emitted]
  190. public readonly VersionHandle Version;
  191. public readonly int Id;
  192. // List of dependent classes - subclasses of this class and classes to which this module is included to (forms a DAG).
  193. private WeakList<RubyClass> _dependentClasses;
  194. #if DEBUG
  195. private int _referringMethodRulesSinceLastUpdate;
  196. public string DebugName {
  197. get {
  198. string name;
  199. if (IsSingletonClass) {
  200. object s = ((RubyClass)this).SingletonClassOf;
  201. RubyModule m = s as RubyModule;
  202. if (m != null) {
  203. name = m.DebugName;
  204. } else {
  205. name = RuntimeHelpers.GetHashCode(s).ToString("x");
  206. }
  207. name = "S(" + name + ")";
  208. } else {
  209. name = _name;
  210. }
  211. return name + " #" + Id;
  212. }
  213. }
  214. #endif
  215. private enum MemberTableState {
  216. Uninitialized = 0,
  217. Initializing = 1,
  218. Initialized = 2
  219. }
  220. // constant table:
  221. private MemberTableState _constantsState = MemberTableState.Uninitialized;
  222. private Action<RubyModule> _constantsInitializer;
  223. private Dictionary<string, ConstantStorage> _constants;
  224. // method table:
  225. private MemberTableState _methodsState = MemberTableState.Uninitialized;
  226. private Dictionary<string, RubyMemberInfo> _methods;
  227. private Action<RubyModule> _methodsInitializer;
  228. // class variable table:
  229. private Dictionary<string, object> _classVariables;
  230. //
  231. // The entire list of modules included in this one. Newly-added mixins are at the front of the array.
  232. // When adding a module that itself contains other modules, Ruby tries to maintain the ordering of the
  233. // contained modules so that method resolution is reasonably consistent.
  234. //
  235. // MRO walk: this, _mixins[0], _mixins[1], ..., _mixins[n-1], super, ...
  236. private RubyModule[]/*!*/ _mixins;
  237. // A list of extension methods included into this type or null if none were included.
  238. // { method-name -> methods }
  239. internal Dictionary<string, List<ExtensionMethodInfo>> _extensionMethods;
  240. #endregion
  241. #region Dynamic Sites
  242. // RubyModule, symbol -> object
  243. private CallSite<Func<CallSite, object, object, object>> _constantMissingCallbackSite;
  244. private CallSite<Func<CallSite, object, object, object>> _methodAddedCallbackSite;
  245. private CallSite<Func<CallSite, object, object, object>> _methodRemovedCallbackSite;
  246. private CallSite<Func<CallSite, object, object, object>> _methodUndefinedCallbackSite;
  247. internal object ConstantMissing(string/*!*/ name) {
  248. return Context.Send(ref _constantMissingCallbackSite, "const_missing", this, name);
  249. }
  250. // Ruby 1.8: called after method is added, except for alias_method which calls it before
  251. // Ruby 1.9: called before method is added
  252. public virtual void MethodAdded(string/*!*/ name) {
  253. Assert.NotNull(name);
  254. Debug.Assert(!IsSingletonClass);
  255. Context.Send(ref _methodAddedCallbackSite, Symbols.MethodAdded, this, name);
  256. }
  257. internal virtual void MethodRemoved(string/*!*/ name) {
  258. Assert.NotNull(name);
  259. Debug.Assert(!IsSingletonClass);
  260. Context.Send(ref _methodRemovedCallbackSite, Symbols.MethodRemoved, this, name);
  261. }
  262. internal virtual void MethodUndefined(string/*!*/ name) {
  263. Assert.NotNull(name);
  264. Debug.Assert(!IsSingletonClass);
  265. Context.Send(ref _methodUndefinedCallbackSite, Symbols.MethodUndefined, this, name);
  266. }
  267. #endregion
  268. public ModuleRestrictions Restrictions {
  269. get { return _restrictions; }
  270. }
  271. internal RubyModule[]/*!*/ Mixins {
  272. get { return _mixins; }
  273. }
  274. public string Name {
  275. get { return _name; }
  276. internal set { _name = value; }
  277. }
  278. public RubyContext/*!*/ Context {
  279. get { return _context; }
  280. }
  281. internal virtual RubyGlobalScope GlobalScope {
  282. get { return null; }
  283. }
  284. internal WeakReference/*!*/ WeakSelf {
  285. get { return _weakSelf; }
  286. }
  287. internal Dictionary<string, List<ExtensionMethodInfo>> ExtensionMethods {
  288. get { return _extensionMethods; }
  289. }
  290. // default allocator:
  291. public RubyModule(RubyClass/*!*/ metaModuleClass)
  292. : this(metaModuleClass, null) {
  293. }
  294. // creates an empty (meta)module:
  295. protected RubyModule(RubyClass/*!*/ metaModuleClass, string name)
  296. : this(metaModuleClass.Context, name, null, null, null, null, null, ModuleRestrictions.None) {
  297. // metaModuleClass represents a subclass of Module or its duplicate (Kernel#dup)
  298. InitializeImmediateClass(metaModuleClass, null);
  299. }
  300. internal RubyModule(RubyContext/*!*/ context, string name, Action<RubyModule> methodsInitializer, Action<RubyModule> constantsInitializer,
  301. RubyModule/*!*/[] expandedMixins, NamespaceTracker namespaceTracker, TypeTracker typeTracker, ModuleRestrictions restrictions) {
  302. Assert.NotNull(context);
  303. Debug.Assert(namespaceTracker == null || typeTracker == null || typeTracker.Type == typeof(object));
  304. Debug.Assert(expandedMixins == null ||
  305. CollectionUtils.TrueForAll(expandedMixins, (m) => m != this && m != null && !m.IsClass && m.Context == context)
  306. );
  307. _context = context;
  308. _name = name;
  309. _methodsInitializer = methodsInitializer;
  310. _constantsInitializer = constantsInitializer;
  311. _namespaceTracker = namespaceTracker;
  312. _typeTracker = typeTracker;
  313. _mixins = expandedMixins ?? EmptyArray;
  314. _restrictions = restrictions;
  315. _weakSelf = new WeakReference(this);
  316. Version = new VersionHandle(Interlocked.Increment(ref _globalMethodVersion));
  317. Version.SetName(name);
  318. Id = Interlocked.Increment(ref _globalModuleId);
  319. }
  320. #region Initialization (thread-safe)
  321. internal bool ConstantInitializationNeeded {
  322. get { return _constantsState == MemberTableState.Uninitialized; }
  323. }
  324. private void InitializeConstantTableNoLock() {
  325. if (!ConstantInitializationNeeded) return;
  326. _constants = new Dictionary<string, ConstantStorage>();
  327. _constantsState = MemberTableState.Initializing;
  328. try {
  329. if (_constantsInitializer != EmptyInitializer) {
  330. if (_constantsInitializer != null) {
  331. Utils.Log(_name ?? "<anonymous>", "CT_INIT");
  332. // TODO: use lock-free operations in initializers
  333. _constantsInitializer(this);
  334. } else if (_typeTracker != null && !_typeTracker.Type.IsInterface) {
  335. // Load types eagerly. We do this only for CLR types that have no constant initializer (not builtins) and
  336. // a constant access is performed (otherwise this method wouldn't be called).
  337. //
  338. // Note: Interfaces cannot declare nested types in C#, we follow the suit here.
  339. // We don't currently need this restriction but once we implement generic type overload inheritance properly
  340. // we would need to deal with inheritance from interfaces, which might be too complex.
  341. //
  342. LoadNestedTypes();
  343. }
  344. }
  345. } finally {
  346. _constantsInitializer = null;
  347. _constantsState = MemberTableState.Initialized;
  348. }
  349. }
  350. internal static readonly Action<RubyModule> EmptyInitializer = (_) => { };
  351. internal bool MethodInitializationNeeded {
  352. get { return _methodsState == MemberTableState.Uninitialized; }
  353. }
  354. private void InitializeMethodTableNoLock() {
  355. if (!MethodInitializationNeeded) return;
  356. InitializeDependencies();
  357. _methods = new Dictionary<string, RubyMemberInfo>();
  358. _methodsState = MemberTableState.Initializing;
  359. try {
  360. if (_methodsInitializer != null) {
  361. Utils.Log(_name ?? "<anonymous>", "MT_INIT");
  362. // TODO: use lock-free operations in initializers?
  363. _methodsInitializer(this);
  364. }
  365. } finally {
  366. _methodsInitializer = null;
  367. _methodsState = MemberTableState.Initialized;
  368. }
  369. }
  370. internal void InitializeMethodsNoLock() {
  371. if (MethodInitializationNeeded) {
  372. InitializeMethodsNoLock(GetUninitializedAncestors(true));
  373. }
  374. }
  375. internal void InitializeMethodsNoLock(IList<RubyModule/*!*/>/*!*/ modules) {
  376. for (int i = modules.Count - 1; i >= 0; i--) {
  377. modules[i].InitializeMethodTableNoLock();
  378. }
  379. }
  380. internal void InitializeConstantsNoLock() {
  381. if (ConstantInitializationNeeded) {
  382. InitializeConstantsNoLock(GetUninitializedAncestors(false));
  383. }
  384. }
  385. internal void InitializeConstantsNoLock(IList<RubyModule/*!*/>/*!*/ modules) {
  386. for (int i = modules.Count - 1; i >= 0; i--) {
  387. modules[i].InitializeConstantTableNoLock();
  388. }
  389. }
  390. private List<RubyModule>/*!*/ GetUninitializedAncestors(bool methods) {
  391. var result = new List<RubyModule>();
  392. result.Add(this);
  393. result.AddRange(_mixins);
  394. var super = GetSuperClass();
  395. while (super != null && (methods ? super.MethodInitializationNeeded : super.ConstantInitializationNeeded)) {
  396. result.Add(super);
  397. result.AddRange(super._mixins);
  398. super = super.SuperClass;
  399. }
  400. return result;
  401. }
  402. private void InitializeClassVariableTable() {
  403. if (_classVariables == null) {
  404. Interlocked.CompareExchange(ref _classVariables, new Dictionary<string, object>(), null);
  405. }
  406. }
  407. private void LoadNestedTypes() {
  408. Context.RequiresClassHierarchyLock();
  409. Debug.Assert(_constants != null && _constants.Count == 0);
  410. // TODO: Inherited generic overloads. We need a custom TypeGroup to do it right - part of the type group might be removed
  411. // TODO: protected types
  412. var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly;
  413. if (Context.DomainManager.Configuration.PrivateBinding) {
  414. bindingFlags |= BindingFlags.NonPublic;
  415. }
  416. // if the constant is redefined/removed from the base class. This is similar to method overload inheritance.
  417. Type[] types = _typeTracker.Type.GetNestedTypes(bindingFlags);
  418. var trackers = new List<TypeTracker>();
  419. var names = new List<string>();
  420. foreach (var type in types) {
  421. TypeTracker tracker = (NestedTypeTracker)MemberTracker.FromMemberInfo(type);
  422. var name = (type.IsGenericType) ? ReflectionUtils.GetNormalizedTypeName(type) : type.Name;
  423. int index = names.IndexOf(name);
  424. if (index != -1) {
  425. trackers[index] = TypeGroup.UpdateTypeEntity(trackers[index], tracker);
  426. names[index] = name;
  427. } else {
  428. trackers.Add(tracker);
  429. names.Add(name);
  430. }
  431. }
  432. for (int i = 0; i < trackers.Count; i++) {
  433. var tracker = trackers[i];
  434. ConstantStorage storage;
  435. if (tracker is TypeGroup) {
  436. storage = new ConstantStorage(tracker, new WeakReference(tracker));
  437. } else {
  438. var module = Context.GetModule(tracker.Type);
  439. storage = new ConstantStorage(module, module.WeakSelf);
  440. }
  441. _constants[names[i]] = storage;
  442. }
  443. }
  444. internal void InitializeMembersFrom(RubyModule/*!*/ module) {
  445. Context.RequiresClassHierarchyLock();
  446. Mutate();
  447. Assert.NotNull(module);
  448. if (module._namespaceTracker != null && _constants == null) {
  449. // initialize the module so that we can copy all constants from it:
  450. module.InitializeConstantsNoLock();
  451. // initialize all ancestors of self:
  452. InitializeConstantsNoLock();
  453. } else {
  454. _constantsInitializer = Utils.CloneInvocationChain(module._constantsInitializer);
  455. _constantsState = module._constantsState;
  456. }
  457. _constants = (module._constants != null) ? new Dictionary<string, ConstantStorage>(module._constants) : null;
  458. // copy namespace members:
  459. if (module._namespaceTracker != null) {
  460. Debug.Assert(_constants != null);
  461. foreach (KeyValuePair<string, object> constant in module._namespaceTracker) {
  462. _constants.Add(constant.Key, new ConstantStorage(constant.Value));
  463. }
  464. }
  465. _methodsInitializer = Utils.CloneInvocationChain(module._methodsInitializer);
  466. _methodsState = module._methodsState;
  467. if (module._methods != null) {
  468. _methods = new Dictionary<string, RubyMemberInfo>(module._methods.Count);
  469. foreach (var method in module._methods) {
  470. _methods[method.Key] = method.Value.Copy(method.Value.Flags, this);
  471. }
  472. } else {
  473. _methods = null;
  474. }
  475. _classVariables = (module._classVariables != null) ? new Dictionary<string, object>(module._classVariables) : null;
  476. _mixins = ArrayUtils.Copy(module._mixins);
  477. // dependentModules - skip
  478. // tracker - skip, .NET members not copied
  479. // TODO:
  480. // - handle overloads cached in groups
  481. // - version updates
  482. MethodsUpdated("InitializeFrom");
  483. }
  484. public void InitializeModuleCopy(RubyModule/*!*/ module) {
  485. if (_context.IsObjectFrozen(this)) {
  486. throw RubyExceptions.CreateTypeError("can't modify frozen Module");
  487. }
  488. using (Context.ClassHierarchyLocker()) {
  489. InitializeMembersFrom(module);
  490. }
  491. }
  492. object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) {
  493. // capture the current immediate class (it can change any time if it not a singleton class)
  494. RubyClass immediate = _immediateClass;
  495. RubyModule result = new RubyModule(immediate.IsSingletonClass ? immediate.SuperClass : immediate, null);
  496. // singleton members are copied here, not in InitializeCopy:
  497. if (copySingletonMembers && immediate.IsSingletonClass) {
  498. var singletonClass = result.GetOrCreateSingletonClass();
  499. using (Context.ClassHierarchyLocker()) {
  500. singletonClass.InitializeMembersFrom(immediate);
  501. }
  502. }
  503. // copy instance variables:
  504. _context.CopyInstanceData(this, result, false);
  505. return result;
  506. }
  507. #endregion
  508. #region Versioning (thread-safe)
  509. // A version of a frozen module can still change if its super-classes/mixins change.
  510. private void Mutate() {
  511. Debug.Assert(!IsDummySingletonClass);
  512. if (IsFrozen) {
  513. throw RubyExceptions.CreateRuntimeError(String.Format("can't modify frozen {0}", IsClass ? "class" : "module"));
  514. }
  515. }
  516. [Conditional("DEBUG")]
  517. internal void OwnedMethodCachedInSite() {
  518. Context.RequiresClassHierarchyLock();
  519. #if DEBUG
  520. _referringMethodRulesSinceLastUpdate++;
  521. #endif
  522. }
  523. internal virtual void InitializeDependencies() {
  524. // nop
  525. }
  526. internal WeakList<RubyClass/*!*/>/*!*/ DependentClasses {
  527. get {
  528. Context.RequiresClassHierarchyLock();
  529. if (_dependentClasses == null) {
  530. _dependentClasses = new WeakList<RubyClass>();
  531. }
  532. return _dependentClasses;
  533. }
  534. }
  535. internal void AddDependentClass(RubyClass/*!*/ dependentClass) {
  536. Context.RequiresClassHierarchyLock();
  537. Assert.NotNull(dependentClass);
  538. foreach (var cls in DependentClasses) {
  539. if (ReferenceEquals(dependentClass, cls)) {
  540. return;
  541. }
  542. }
  543. DependentClasses.Add(dependentClass.WeakSelf);
  544. }
  545. private void IncrementMethodVersion() {
  546. if (IsClass) {
  547. Version.Method = Interlocked.Increment(ref _globalMethodVersion);
  548. }
  549. }
  550. internal void MethodsUpdated(string/*!*/ reason) {
  551. Context.RequiresClassHierarchyLock();
  552. int affectedModules = 0;
  553. int affectedRules = 0;
  554. Func<RubyModule, bool> visitor = (module) => {
  555. module.IncrementMethodVersion();
  556. #if DEBUG
  557. affectedModules++;
  558. affectedRules += module._referringMethodRulesSinceLastUpdate;
  559. module._referringMethodRulesSinceLastUpdate = 0;
  560. #endif
  561. // TODO (opt?): stop updating if a class that defines a method of the same name is reached.
  562. return false;
  563. };
  564. visitor(this);
  565. ForEachRecursivelyDependentClass(visitor);
  566. Utils.Log(String.Format("{0,-50} {1,-30} affected={2,-5} rules={3,-5}", Name, reason, affectedModules, affectedRules), "UPDATED");
  567. }
  568. /// <summary>
  569. /// Calls given action on all modules that are directly or indirectly nested into this module.
  570. /// </summary>
  571. private bool ForEachRecursivelyDependentClass(Func<RubyModule, bool>/*!*/ action) {
  572. Context.RequiresClassHierarchyLock();
  573. if (_dependentClasses != null) {
  574. foreach (var cls in _dependentClasses) {
  575. if (action(cls)) {
  576. return true;
  577. }
  578. if (cls.ForEachRecursivelyDependentClass(action)) {
  579. return true;
  580. }
  581. }
  582. }
  583. return false;
  584. }
  585. #endregion
  586. #region IRubyObject Members
  587. // thread-safe:
  588. public RubyClass ImmediateClass {
  589. get {
  590. return _immediateClass;
  591. }
  592. set {
  593. throw new InvalidOperationException("Cannot change the immediate class of a module");
  594. }
  595. }
  596. // thread-safe:
  597. public RubyInstanceData TryGetInstanceData() {
  598. return _instanceData;
  599. }
  600. // thread-safe:
  601. public RubyInstanceData GetInstanceData() {
  602. return RubyOps.GetInstanceData(ref _instanceData);
  603. }
  604. // thread-safe:
  605. public virtual bool IsFrozen {
  606. get { return IsModuleFrozen; }
  607. }
  608. // thread-safe: _instanceData cannot be unset
  609. internal bool IsModuleFrozen {
  610. get { return _instanceData != null && _instanceData.IsFrozen; }
  611. }
  612. // thread-safe:
  613. public void Freeze() {
  614. GetInstanceData().Freeze();
  615. }
  616. // thread-safe:
  617. public bool IsTainted {
  618. get { return GetInstanceData().IsTainted; }
  619. set { GetInstanceData().IsTainted = value; }
  620. }
  621. // thread-safe:
  622. public bool IsUntrusted {
  623. get { return GetInstanceData().IsUntrusted; }
  624. set { GetInstanceData().IsUntrusted = value; }
  625. }
  626. int IRubyObject.BaseGetHashCode() {
  627. return base.GetHashCode();
  628. }
  629. bool IRubyObject.BaseEquals(object other) {
  630. return base.Equals(other);
  631. }
  632. string/*!*/ IRubyObject.BaseToString() {
  633. return base.ToString();
  634. }
  635. public override string/*!*/ ToString() {
  636. return _name ?? "<anonymous>";
  637. }
  638. #endregion
  639. #region Factories (thread-safe)
  640. // Ruby constructor:
  641. public static object CreateAnonymousModule(RubyScope/*!*/ scope, BlockParam body, RubyClass/*!*/ self) {
  642. RubyModule newModule = new RubyModule(self, null);
  643. return (body != null) ? RubyUtils.EvaluateInModule(newModule, body, null, newModule) : newModule;
  644. }
  645. // thread safe:
  646. public RubyClass/*!*/ GetOrCreateSingletonClass() {
  647. if (IsDummySingletonClass) {
  648. throw new InvalidOperationException("Dummy singleton class has no singleton class");
  649. }
  650. RubyClass immediate = _immediateClass;
  651. RubyClass singletonSuper;
  652. RubyClass singletonImmediate;
  653. if (!immediate.IsSingletonClass) {
  654. // finish module singleton initialization:
  655. Debug.Assert(!IsClass);
  656. singletonSuper = immediate;
  657. singletonImmediate = immediate.GetDummySingletonClass();
  658. } else if (immediate.IsDummySingletonClass) {
  659. // expanding singleton chain:
  660. singletonSuper = immediate.SuperClass;
  661. singletonImmediate = immediate;
  662. } else {
  663. return immediate;
  664. }
  665. var singleton = CreateSingletonClass(singletonSuper, null);
  666. singleton.InitializeImmediateClass(singletonImmediate);
  667. Interlocked.CompareExchange(ref _immediateClass, singleton, immediate);
  668. Debug.Assert(_immediateClass.IsSingletonClass && !_immediateClass.IsDummySingletonClass);
  669. return _immediateClass;
  670. }
  671. /// <summary>
  672. /// Create a new singleton class for this module.
  673. /// Doesn't attach this module to it yet, the caller needs to do so.
  674. /// </summary>
  675. /// <remarks>Thread safe.</remarks>
  676. internal RubyClass/*!*/ CreateSingletonClass(RubyClass/*!*/ superClass, Action<RubyModule> trait) {
  677. // Note that in MRI, member tables of dummy singleton are shared with the class the dummy is singleton for
  678. // This is obviously an implementation detail leaking to the language and we don't support that.
  679. // real class object and it's singleton share the tracker:
  680. TypeTracker tracker = (IsSingletonClass) ? null : _typeTracker;
  681. // Singleton should have the same restrictions as the module it is singleton for.
  682. // Reason: We need static methods of builtins (e.g. Object#Equals) not to be exposed under Ruby names (Object#equals).
  683. // We also want static methods of non-builtins to be visible under both CLR and Ruby names.
  684. var result = new RubyClass(
  685. Context, null, null, this, trait, null, null, superClass, null, tracker, null, false, true, this.Restrictions
  686. );
  687. #if DEBUG
  688. result.Version.SetName(result.DebugName);
  689. #endif
  690. return result;
  691. }
  692. #endregion
  693. #region Ancestors (thread-safe)
  694. // Return true from action to terminate enumeration.
  695. public bool ForEachAncestor(bool inherited, Func<RubyModule/*!*/, bool>/*!*/ action) {
  696. Context.RequiresClassHierarchyLock();
  697. if (inherited) {
  698. return ForEachAncestor(action);
  699. } else {
  700. return ForEachDeclaredAncestor(action);
  701. }
  702. }
  703. internal virtual bool ForEachAncestor(Func<RubyModule/*!*/, bool>/*!*/ action) {
  704. Context.RequiresClassHierarchyLock();
  705. return ForEachDeclaredAncestor(action);
  706. }
  707. internal bool ForEachDeclaredAncestor(Func<RubyModule/*!*/, bool>/*!*/ action) {
  708. Context.RequiresClassHierarchyLock();
  709. // this module:
  710. if (action(this)) return true;
  711. // mixins:
  712. foreach (RubyModule m in _mixins) {
  713. if (action(m)) return true;
  714. }
  715. return false;
  716. }
  717. #endregion
  718. #region Constants (thread-safe)
  719. // Value of constant that is to be auto-loaded on first use.
  720. private sealed class AutoloadedConstant {
  721. private readonly MutableString/*!*/ _path;
  722. private bool _loaded;
  723. // File already loaded? An auto-loaded constant can be referenced by mutliple classes (via duplication).
  724. // After the constant is accessed in one of them and the file is loaded access to the other duplicates doesn't trigger file load.
  725. public bool Loaded { get { return _loaded; } }
  726. public MutableString/*!*/ Path { get { return _path; } }
  727. public AutoloadedConstant(MutableString/*!*/ path) {
  728. Assert.NotNull(path);
  729. Debug.Assert(path.IsFrozen);
  730. _path = path;
  731. }
  732. public bool Load(RubyGlobalScope/*!*/ autoloadScope) {
  733. if (_loaded) {
  734. return false;
  735. }
  736. using (autoloadScope.Context.ClassHierarchyUnlocker()) {
  737. _loaded = true;
  738. return autoloadScope.Context.Loader.LoadFile(autoloadScope.Scope, null, _path, LoadFlags.Require);
  739. }
  740. }
  741. }
  742. public string/*!*/ MakeNestedModuleName(string nestedModuleSimpleName) {
  743. return (IsObjectClass || nestedModuleSimpleName == null) ?
  744. nestedModuleSimpleName :
  745. _name + "::" + nestedModuleSimpleName;
  746. }
  747. // not thread-safe
  748. public void ForEachConstant(bool inherited, Func<RubyModule/*!*/, string/*!*/, object, bool>/*!*/ action) {
  749. Context.RequiresClassHierarchyLock();
  750. ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) {
  751. // notification that we entered the module (it could have no constant):
  752. if (action(module, null, Missing.Value)) return true;
  753. return module.EnumerateConstants(action);
  754. });
  755. }
  756. // thread-safe:
  757. public void SetConstant(string/*!*/ name, object value) {
  758. using (Context.ClassHierarchyLocker()) {
  759. SetConstantNoLock(name, value);
  760. }
  761. }
  762. internal void Publish(string/*!*/ name) {
  763. RubyOps.ScopeSetMember(_context.TopGlobalScope, name, this);
  764. }
  765. private void SetConstantNoLock(string/*!*/ name, object value) {
  766. Mutate();
  767. SetConstantNoMutateNoLock(name, value);
  768. }
  769. internal void SetConstantNoMutateNoLock(string/*!*/ name, object value) {
  770. Context.RequiresClassHierarchyLock();
  771. InitializeConstantsNoLock();
  772. _context.ConstantAccessVersion++;
  773. _constants[name] = new ConstantStorage(value);
  774. }
  775. /// <summary>
  776. /// Sets constant of this module.
  777. /// Returns true if the constant is already defined in the module and it is not an autoloaded constant.
  778. /// </summary>
  779. /// <remarks>
  780. /// Thread safe.
  781. /// </remarks>
  782. public bool SetConstantChecked(string/*!*/ name, object value) {
  783. using (Context.ClassHierarchyLocker()) {
  784. ConstantStorage existing;
  785. var result = TryLookupConstantNoLock(false, false, null, name, out existing);
  786. SetConstantNoLock(name, value);
  787. return result == ConstantLookupResult.Found;
  788. }
  789. }
  790. // thread-safe:
  791. public void SetAutoloadedConstant(string/*!*/ name, MutableString/*!*/ path) {
  792. ConstantStorage dummy;
  793. if (!TryGetConstant(null, name, out dummy)) {
  794. SetConstant(name, new AutoloadedConstant(MutableString.Create(path).Freeze()));
  795. }
  796. }
  797. // thread-safe:
  798. public MutableString GetAutoloadedConstantPath(string/*!*/ name) {
  799. using (Context.ClassHierarchyLocker()) {
  800. ConstantStorage storage;
  801. AutoloadedConstant autoloaded;
  802. return (TryGetConstantNoAutoloadCheck(name, out storage)
  803. && (autoloaded = storage.Value as AutoloadedConstant) != null
  804. && !autoloaded.Loaded) ?
  805. autoloaded.Path : null;
  806. }
  807. }
  808. internal bool TryResolveConstant(RubyContext/*!*/ callerContext, RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
  809. return callerContext != Context ?
  810. TryResolveConstant(autoloadScope, name, out value) :
  811. TryResolveConstantNoLock(autoloadScope, name, out value);
  812. }
  813. public bool TryGetConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out object value) {
  814. ConstantStorage storage;
  815. var result = TryGetConstant(autoloadScope, name, out storage);
  816. value = storage.Value;
  817. return result;
  818. }
  819. /// <summary>
  820. /// Get constant defined in this module.
  821. /// </summary>
  822. internal bool TryGetConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
  823. using (Context.ClassHierarchyLocker()) {
  824. return TryGetConstantNoLock(autoloadScope, name, out value);
  825. }
  826. }
  827. /// <summary>
  828. /// Get constant defined in this module.
  829. /// </summary>
  830. internal bool TryGetConstantNoLock(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
  831. Context.RequiresClassHierarchyLock();
  832. return TryLookupConstantNoLock(false, false, autoloadScope, name, out value) != ConstantLookupResult.NotFound;
  833. }
  834. /// <summary>
  835. /// Get constant defined in this module or any of its ancestors.
  836. /// Autoloads if autoloadScope is not null.
  837. /// </summary>
  838. /// <remarks>
  839. /// Thread safe.
  840. /// </remarks>
  841. internal bool TryResolveConstant(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
  842. using (Context.ClassHierarchyLocker()) {
  843. return TryResolveConstantNoLock(autoloadScope, name, out value);
  844. }
  845. }
  846. /// <summary>
  847. /// Get constant defined in this module or any of its ancestors.
  848. /// </summary>
  849. internal bool TryResolveConstantNoLock(RubyGlobalScope autoloadScope, string/*!*/ name, out ConstantStorage value) {
  850. Context.RequiresClassHierarchyLock();
  851. return TryLookupConstantNoLock(true, true, autoloadScope, name, out value) != ConstantLookupResult.NotFound;
  852. }
  853. private enum ConstantLookupResult {
  854. NotFound = 0,
  855. Found = 1,
  856. FoundAutoload = 2,
  857. }
  858. private ConstantLookupResult TryLookupConstantNoLock(bool included, bool inherited, RubyGlobalScope autoloadScope,
  859. string/*!*/ name, out ConstantStorage value) {
  860. Context.RequiresClassHierarchyLock();
  861. Debug.Assert(included || !inherited);
  862. value = default(ConstantStorage);
  863. while (true) {
  864. ConstantStorage result;
  865. RubyModule owner = included ?
  866. TryResolveConstantNoAutoloadCheck(inherited, name, out result) :
  867. (TryGetConstantNoAutoloadCheck(name, out result) ? this : null);
  868. if (owner == null) {
  869. return ConstantLookupResult.NotFound;
  870. }
  871. var autoloaded = result.Value as AutoloadedConstant;
  872. if (autoloaded == null) {
  873. value = result;
  874. return ConstantLookupResult.Found;
  875. }
  876. if (autoloadScope == null) {
  877. return ConstantLookupResult.FoundAutoload;
  878. }
  879. if (autoloadScope.Context != Context) {
  880. throw RubyExceptions.CreateTypeError(String.Format("Cannot autoload constants to a foreign runtime #{0}", autoloadScope.Context.RuntimeId));
  881. }
  882. // autoloaded constants are removed before the associated file is loaded:
  883. object _;
  884. owner.TryRemoveConstantNoLock(name, out _);
  885. // load file and try lookup again (releases the class hierarchy lock when loading the file):
  886. if (!autoloaded.Load(autoloadScope)) {
  887. return ConstantLookupResult.NotFound;
  888. }
  889. }
  890. }
  891. // Returns the owner of the constant or null if the constant is not found.
  892. private RubyModule TryResolveConstantNoAutoloadCheck(bool inherited, string/*!*/ name, out ConstantStorage value) {
  893. Context.RequiresClassHierarchyLock();
  894. var storage = default(ConstantStorage);
  895. RubyModule owner = null;
  896. if (ForEachAncestor(inherited, (module) => (owner = module).TryGetConstantNoAutoloadCheck(name, out storage))) {
  897. value = storage;
  898. return owner;
  899. } else {
  900. value = storage;
  901. return null;
  902. }
  903. }
  904. // Returns the owner of the constant (this module) or null if the constant is not found.
  905. internal bool TryGetConstantNoAutoloadCheck(string/*!*/ name, out ConstantStorage storage) {
  906. Context.RequiresClassHierarchyLock();
  907. if (name.Length == 0) {
  908. storage = default(ConstantStorage);
  909. return false;
  910. }
  911. InitializeConstantsNoLock();
  912. if (_constants.TryGetValue(name, out storage)) {
  913. if (storage.IsRemoved) {
  914. storage = default(ConstantStorage);
  915. return false;
  916. } else {
  917. return true;
  918. }
  919. }
  920. if (_namespaceTracker != null) {
  921. object value;
  922. if (_namespaceTracker.TryGetValue(name, out value)) {
  923. storage = new ConstantStorage( _context.TrackerToModule(value));
  924. return true;
  925. }
  926. }
  927. storage = default(ConstantStorage);
  928. return false;
  929. }
  930. // Direct lookup into the constant table (if it exists).
  931. internal bool TryGetConstantNoAutoloadNoInit(string/*!*/ name, out ConstantStorage storage) {
  932. Context.RequiresClassHierarchyLock();
  933. storage = default(ConstantStorage);
  934. return _constants != null && _constants.TryGetValue(name, out storage) && !storage.IsRemoved;
  935. }
  936. // thread-safe:
  937. public bool TryRemoveConstant(string/*!*/ name, out object value) {
  938. using (Context.ClassHierarchyLocker()) {
  939. return TryRemoveConstantNoLock(name, out value);
  940. }
  941. }
  942. private bool TryRemoveConstantNoLock(string/*!*/ name, out object value) {
  943. Context.RequiresClassHierarchyLock();
  944. InitializeConstantsNoLock();
  945. bool result;
  946. ConstantStorage storage;
  947. if (_constants.TryGetValue(name, out storage)) {
  948. if (storage.IsRemoved) {
  949. value = null;
  950. return false;
  951. } else {
  952. value = storage.Value;
  953. result = true;
  954. }
  955. } else {
  956. value = null;
  957. result = false;
  958. }
  959. object namespaceValue;
  960. if (_namespaceTracker != null && _namespaceTracker.TryGetValue(name, out namespaceValue)) {
  961. _constants[name] = ConstantStorage.Removed;
  962. _context.ConstantAccessVersion++;
  963. value = namespaceValue;
  964. result = true;
  965. } else if (result) {
  966. _constants.Remove(name);
  967. _context.ConstantAccessVersion++;
  968. }
  969. return result;
  970. }
  971. public bool EnumerateConstants(Func<RubyModule, string, object, bool>/*!*/ action) {
  972. Context.RequiresClassHierarchyLock();
  973. InitializeConstantsNoLock();
  974. foreach (var constant in _constants) {
  975. var name = constant.Key;
  976. var storage = constant.Value;
  977. if (!storage.IsRemoved && action(this, name, storage.Value)) {
  978. return true;
  979. }
  980. }
  981. if (_namespaceTracker != null) {
  982. foreach (KeyValuePair<string, object> constant in _namespaceTracker) {
  983. string name = constant.Key;
  984. // we check if we haven't already yielded the value so that we don't yield values hidden by a user defined constant:
  985. if (!_constants.ContainsKey(name) && action(this, name, constant.Value)) {
  986. return true;
  987. }
  988. }
  989. }
  990. return false;
  991. }
  992. #endregion
  993. #region Methods (thread-safe)
  994. // not thread-safe:
  995. public void ForEachInstanceMethod(bool inherited, Func<RubyModule/*!*/, string/*!*/, RubyMemberInfo, bool>/*!*/ action) {
  996. Context.RequiresClassHierarchyLock();
  997. ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) {
  998. // Skip CLR modules (methods declared on CLR modules have already been looked for in the class).
  999. // If 'this' is a CLR module, we want to visit all mixed-in methods.
  1000. if (module.IsClrModule && !this.IsClrModule) return false;
  1001. // notification that we entered the module (it could have no method):
  1002. if (action(module, null, null)) return true;
  1003. return module.EnumerateMethods(action);
  1004. });
  1005. }
  1006. // thread-safe:
  1007. public void AddMethodAlias(string/*!*/ newName, string/*!*/ oldName) {
  1008. // MRI 1.8: if (newName == oldName) return;
  1009. // MRI 1.9: no check
  1010. RubyMemberInfo method;
  1011. using (Context.ClassHierarchyLocker()) {
  1012. // MRI: aliases a super-forwarder not the real method.
  1013. method = ResolveMethodNoLock(oldName, VisibilityContext.AllVisible, MethodLookup.FallbackToObject | MethodLookup.ReturnForwarder).Info;
  1014. if (method == null) {
  1015. throw RubyExceptions.CreateUndefinedMethodError(this, oldName);
  1016. }
  1017. // Alias preserves visibility and declaring module even though the alias is declared in a different module (e.g. subclass) =>
  1018. // we can share method info (in fact, sharing is sound with Method#== semantics - it returns true on aliased methods).
  1019. //
  1020. // CLR members:
  1021. // Detaches the member from its underlying type (by creating a copy).
  1022. // Note: We need to copy overload group since otherwise it might mess up caching if the alias is defined in a sub-module and
  1023. // overloads of the same name that are not included in the overload group are inherited to this module.
  1024. // EnumerateMethods also relies on overload groups only representing cached CLR members.
  1025. if (!method.IsRubyMember) {
  1026. SetMethodNoEventNoLock(Context, newName, method.Copy(method.Flags, method.DeclaringModule));
  1027. } else {
  1028. SetMethodNoEventNoLock(Context, newName, method);
  1029. }
  1030. }
  1031. MethodAdded(newName);
  1032. }
  1033. // Module#define_method:
  1034. public void SetDefinedMethodNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method, RubyMethodVisibility visibility) {
  1035. // CLR members: Detaches the member from its underlying type (by creating a copy).
  1036. // Note: Method#== returns false on defined methods and redefining the original method doesn't affect the new one:
  1037. SetMethodNoEventNoLock(callerContext, name, method.Copy((RubyMemberFlags)visibility, this));
  1038. }
  1039. // Module#module_function/private/protected/public:
  1040. public void SetVisibilityNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method, RubyMethodVisibility visibility) {
  1041. Context.RequiresClassHierarchyLock();
  1042. RubyMemberInfo existing;
  1043. bool skipHidden = false;
  1044. if (TryGetMethod(name, ref skipHidden, out existing)) {
  1045. // CLR members: Detaches the member from its underlying type (by creating a copy).
  1046. SetMethodNoEventNoLock(callerContext, name, method.Copy((RubyMemberFlags)visibility, this));
  1047. } else {
  1048. SetMethodNoEventNoLock(callerContext, name, new SuperForwarderInfo((RubyMemberFlags)visibility, method.DeclaringModule, name));
  1049. }
  1050. }
  1051. // Module#module_function:
  1052. public void SetModuleFunctionNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1053. Debug.Assert(!IsClass);
  1054. // CLR members: Detaches the member from its underlying type (by creating a copy).
  1055. // TODO: check for CLR instance members, it should be an error to call module_function on them:
  1056. var singletonClass = GetOrCreateSingletonClass();
  1057. singletonClass.SetMethodNoEventNoLock(callerContext, name, method.Copy(RubyMemberFlags.Public, singletonClass));
  1058. }
  1059. // thread-safe:
  1060. public void AddMethod(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1061. Assert.NotNull(name, method);
  1062. Mutate();
  1063. SetMethodNoEvent(callerContext, name, method);
  1064. MethodAdded(name);
  1065. }
  1066. // thread-safe:
  1067. public void SetMethodNoEvent(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1068. using (Context.ClassHierarchyLocker()) {
  1069. SetMethodNoEventNoLock(callerContext, name, method);
  1070. }
  1071. }
  1072. public void SetMethodNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1073. Mutate();
  1074. SetMethodNoMutateNoEventNoLock(callerContext, name, method);
  1075. }
  1076. internal void SetMethodNoMutateNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1077. Context.RequiresClassHierarchyLock();
  1078. Assert.NotNull(name, method);
  1079. if (callerContext != _context) {
  1080. throw RubyExceptions.CreateTypeError(String.Format("Cannot define a method on a {0} `{1}' defined in a foreign runtime #{2}",
  1081. IsClass ? "class" : "module", _name, _context.RuntimeId));
  1082. }
  1083. if (method.IsUndefined && name == Symbols.Initialize) {
  1084. throw RubyExceptions.CreateTypeError("Cannot undefine `initialize' method");
  1085. }
  1086. PrepareMethodUpdate(name, method);
  1087. InitializeMethodsNoLock();
  1088. _methods[name] = method;
  1089. }
  1090. internal void AddExtensionMethodsNoLock(List<ExtensionMethodInfo>/*!*/ extensions) {
  1091. Context.RequiresClassHierarchyLock();
  1092. PrepareExtensionMethodsUpdate(extensions);
  1093. if (_extensionMethods == null) {
  1094. _extensionMethods = new Dictionary<string, List<ExtensionMethodInfo>>();
  1095. }
  1096. foreach (var extension in extensions) {
  1097. List<ExtensionMethodInfo> overloads;
  1098. if (!_extensionMethods.TryGetValue(extension.Method.Name, out overloads)) {
  1099. _extensionMethods.Add(extension.Method.Name, overloads = new List<ExtensionMethodInfo>());
  1100. }
  1101. // If the signature of the extension is the same as other overloads the overload resolver should prefer non extension method.
  1102. overloads.Add(extension);
  1103. }
  1104. }
  1105. // TODO: constraints
  1106. private static bool IsPartiallyInstantiated(Type/*!*/ type) {
  1107. if (type.IsGenericParameter) {
  1108. return false;
  1109. }
  1110. if (type.IsArray) {
  1111. return !type.GetElementType().IsGenericParameter;
  1112. }
  1113. foreach (var arg in type.GetGenericArguments()) {
  1114. if (!arg.IsGenericParameter) {
  1115. Debug.Assert(arg.DeclaringMethod != null);
  1116. return true;
  1117. }
  1118. }
  1119. return false;
  1120. }
  1121. internal virtual void PrepareExtensionMethodsUpdate(List<ExtensionMethodInfo>/*!*/ extensions) {
  1122. if (_dependentClasses != null) {
  1123. foreach (var cls in _dependentClasses) {
  1124. cls.PrepareExtensionMethodsUpdate(extensions);
  1125. }
  1126. }
  1127. }
  1128. internal virtual void PrepareMethodUpdate(string/*!*/ methodName, RubyMemberInfo/*!*/ method) {
  1129. InitializeMethodsNoLock();
  1130. // Prepare all classes where this module is included for a method update.
  1131. // TODO (optimization): we might end up walking some classes multiple times, could we mark them somehow as visited?
  1132. if (_dependentClasses != null) {
  1133. foreach (var cls in _dependentClasses) {
  1134. cls.PrepareMethodUpdate(methodName, method, 0);
  1135. }
  1136. }
  1137. }
  1138. // Returns max level in which a group has been invalidated.
  1139. internal int InvalidateGroupsInDependentClasses(string/*!*/ methodName, int maxLevel) {
  1140. int result = -1;
  1141. if (_dependentClasses != null) {
  1142. foreach (var cls in _dependentClasses) {
  1143. result = Math.Max(result, cls.InvalidateGroupsInSubClasses(methodName, maxLevel));
  1144. }
  1145. }
  1146. return result;
  1147. }
  1148. internal bool TryGetDefinedMethod(string/*!*/ name, out RubyMemberInfo method) {
  1149. Context.RequiresClassHierarchyLock();
  1150. if (_methods == null) {
  1151. method = null;
  1152. return false;
  1153. }
  1154. return _methods.TryGetValue(name, out method);
  1155. }
  1156. internal bool TryGetDefinedMethod(string/*!*/ name, ref bool skipHidden, out RubyMemberInfo method) {
  1157. Context.RequiresClassHierarchyLock();
  1158. if (TryGetDefinedMethod(name, out method)) {
  1159. if (method.IsHidden || skipHidden && !method.IsRemovable) {
  1160. skipHidden = true;
  1161. method = null;
  1162. return false;
  1163. } else {
  1164. return true;
  1165. }
  1166. }
  1167. return false;
  1168. }
  1169. internal IEnumerable<KeyValuePair<string/*!*/, RubyMemberInfo/*!*/>>/*!*/ GetMethods() {
  1170. Context.RequiresClassHierarchyLock();
  1171. return _methods;
  1172. }
  1173. /// <summary>
  1174. /// Direct addition to the method table. Used only for core method table operations.
  1175. /// Do not use unless absolutely sure there is no overriding method used in a dynamic site.
  1176. /// </summary>
  1177. internal void AddMethodNoCacheInvalidation(string/*!*/ name, RubyMemberInfo/*!*/ method) {
  1178. Context.RequiresClassHierarchyLock();
  1179. Debug.Assert(_methods != null);
  1180. _methods.Add(name, method);
  1181. }
  1182. /// <summary>
  1183. /// Direct removal from the method table. Used only for core method table operations.
  1184. /// </summary>
  1185. internal bool RemoveMethodNoCacheInvalidation(string/*!*/ name) {
  1186. Context.RequiresClassHierarchyLock();
  1187. Debug.Assert(_methods != null);
  1188. return _methods.Remove(name);
  1189. }
  1190. // thread-safe:
  1191. public bool RemoveMethod(string/*!*/ name) {
  1192. if (RemoveMethodNoEvent(name)) {
  1193. MethodRemoved(name);
  1194. return true;
  1195. }
  1196. return false;
  1197. }
  1198. // thread-safe:
  1199. private bool RemoveMethodNoEvent(string/*!*/ name) {
  1200. Mutate();
  1201. using (Context.ClassHierarchyLocker()) {
  1202. InitializeMethodsNoLock();
  1203. RubyMemberInfo method;
  1204. if (_methods.TryGetValue(name, out method)) {
  1205. if (method.IsHidden || method.IsUndefined) {
  1206. return false;
  1207. } else if (IsBasicObjectClass && name == Symbols.Initialize) {
  1208. // We prohibit removing Object#initialize to simplify object construction logic.
  1209. return false;
  1210. } else if (method.IsRemovable) {
  1211. // Method is used in a dynamic site or group => update version of all dependencies of this module.
  1212. if (method.InvalidateSitesOnOverride || method.InvalidateGroupsOnRemoval) {
  1213. MethodsUpdated("RemoveMethod: " + name);
  1214. }
  1215. // Method hides CLR overloads => update method groups in all dependencies of this module.
  1216. // TODO (opt): Do not update the entire subtree, update subtrees of all invalidated groups.
  1217. // TODO (opt): We can calculate max-level but it requires maintanance whenever a method is overridden
  1218. // and whenever method group is lazily created (TryGetClrMethod).
  1219. if (method.InvalidateGroupsOnRemoval) {
  1220. InvalidateGroupsInDependentClasses(name, Int32.MaxValue);
  1221. }
  1222. _methods.Remove(name);
  1223. } else {
  1224. SetMethodNoEventNoLock(Context, name, RubyMemberInfo.HiddenMethod);
  1225. }
  1226. return true;
  1227. } else if (TryGetClrMember(name, false, out method)) {
  1228. Debug.Assert(!method.IsRemovable);
  1229. SetMethodNoEventNoLock(Context, name, RubyMemberInfo.HiddenMethod);
  1230. return true;
  1231. } else {
  1232. return false;
  1233. }
  1234. }
  1235. }
  1236. // thread-safe:
  1237. public void UndefineMethod(string/*!*/ name) {
  1238. UndefineMethodNoEvent(name);
  1239. MethodUndefined(name);
  1240. }
  1241. // thread-safe:
  1242. public void UndefineMethodNoEvent(string/*!*/ name) {
  1243. SetMethodNoEvent(Context, name, RubyMethodInfo.UndefinedMethod);
  1244. }
  1245. // thread-safe:
  1246. public void HideMethod(string/*!*/ name) {
  1247. SetMethodNoEvent(Context, name, RubyMethodInfo.HiddenMethod);
  1248. }
  1249. // thread-safe:
  1250. public MethodResolutionResult ResolveMethodForSite(string/*!*/ name, VisibilityContext visibility) {
  1251. using (Context.ClassHierarchyLocker()) {
  1252. return ResolveMethodForSiteNoLock(name, visibility);
  1253. }
  1254. }
  1255. // thread-safe:
  1256. public MethodResolutionResult ResolveMethod(string/*!*/ name, VisibilityContext visibility) {
  1257. using (Context.ClassHierarchyLocker()) {
  1258. return ResolveMethodNoLock(name, visibility);
  1259. }
  1260. }
  1261. public MethodResolutionResult ResolveMethodForSiteNoLock(string/*!*/ name, VisibilityContext visibility) {
  1262. return ResolveMethodForSiteNoLock(name, visibility, MethodLookup.Default);
  1263. }
  1264. internal MethodResolutionResult ResolveMethodForSiteNoLock(string/*!*/ name, VisibilityContext visibility, MethodLookup options) {
  1265. return ResolveMethodNoLock(name, visibility, options).InvalidateSitesOnOverride();
  1266. }
  1267. public MethodResolutionResult ResolveMethodNoLock(string/*!*/ name, VisibilityContext visibility) {
  1268. return ResolveMethodNoLock(name, visibility, MethodLookup.Default);
  1269. }
  1270. public MethodResolutionResult ResolveMethodNoLock(string/*!*/ name, VisibilityContext visibility, MethodLookup options) {
  1271. Context.RequiresClassHierarchyLock();
  1272. Assert.NotNull(name);
  1273. InitializeMethodsNoLock();
  1274. RubyMemberInfo info = null;
  1275. RubyModule owner = null;
  1276. bool skipHidden = false;
  1277. bool foundCallerSelf = false;
  1278. MethodResolutionResult result;
  1279. if (ForEachAncestor((module) => {
  1280. owner = module;
  1281. foundCallerSelf |= module == visibility.Class;
  1282. return module.TryGetMethod(name, ref skipHidden, (options & MethodLookup.Virtual) != 0, out info);
  1283. })) {
  1284. if (info == null || info.IsUndefined) {
  1285. result = MethodResolutionResult.NotFound;
  1286. } else if (!IsMethodVisible(info, owner, visibility, foundCallerSelf)) {
  1287. result = new MethodResolutionResult(info, owner, false);
  1288. } else if (info.IsSuperForwarder) {
  1289. if ((options & MethodLookup.ReturnForwarder) != 0) {
  1290. result = new MethodResolutionResult(info, owner, true);
  1291. } else {
  1292. // start again with owner's super ancestor and ignore visibility:
  1293. result = owner.ResolveSuperMethodNoLock(((SuperForwarderInfo)info).SuperName, owner);
  1294. }
  1295. } else {
  1296. result = new MethodResolutionResult(info, owner, true);
  1297. }
  1298. } else {
  1299. result = MethodResolutionResult.NotFound;
  1300. }
  1301. // TODO: BasicObject
  1302. // Note: all classes include Object in ancestors, so we don't need to search it again:
  1303. if (!result.Found && (options & MethodLookup.FallbackToObject) != 0 && !IsClass) {
  1304. return _context.ObjectClass.ResolveMethodNoLock(name, visibility, options & ~MethodLookup.FallbackToObject);
  1305. }
  1306. return result;
  1307. }
  1308. private bool IsMethodVisible(RubyMemberInfo/*!*/ method, RubyModule/*!*/ owner, VisibilityContext visibility, bool foundCallerSelf) {
  1309. // Visibility not constrained by a class:
  1310. // - call with implicit self => all methods are visible.
  1311. // - interop call => only public methods are visible.
  1312. if (visibility.Class == null) {
  1313. return visibility.IsVisible(method.Visibility);
  1314. }
  1315. if (method.Visibility == RubyMethodVisibility.Protected) {
  1316. // A protected method is visible if the caller's self immediate class is a descendant of the method owner.
  1317. if (foundCallerSelf) {
  1318. return true;
  1319. }
  1320. // walk ancestors from caller's self class (visibilityContext)
  1321. // until the method owner is found or this module is found (this module is a descendant of the owner):
  1322. return visibility.Class.ForEachAncestor((module) => module == owner || module == this);
  1323. }
  1324. return method.Visibility == RubyMethodVisibility.Public;
  1325. }
  1326. // skip one method in the method resolution order (MRO)
  1327. public MethodResolutionResult ResolveSuperMethodNoLock(string/*!*/ name, RubyModule/*!*/ callerModule) {
  1328. Context.RequiresClassHierarchyLock();
  1329. Assert.NotNull(name, callerModule);
  1330. InitializeMethodsNoLock();
  1331. RubyMemberInfo info = null;
  1332. RubyModule owner = null;
  1333. bool foundModule = false;
  1334. bool skipHidden = false;
  1335. // start searching for the method in the MRO parent of the declaringModule:
  1336. if (ForEachAncestor((module) => {
  1337. if (module == callerModule) {
  1338. foundModule = true;
  1339. return false;
  1340. }
  1341. owner = module;
  1342. return foundModule && module.TryGetMethod(name, ref skipHidden, out info) && !info.IsSuperForwarder;
  1343. }) && !info.IsUndefined) {
  1344. return new MethodResolutionResult(info, owner, true);
  1345. }
  1346. return MethodResolutionResult.NotFound;
  1347. }
  1348. // thread-safe:
  1349. public RubyMemberInfo GetMethod(string/*!*/ name) {
  1350. ContractUtils.RequiresNotNull(name, "name");
  1351. using (Context.ClassHierarchyLocker()) {
  1352. InitializeMethodsNoLock();
  1353. RubyMemberInfo method;
  1354. bool skipHidden = false;
  1355. TryGetMethod(name, ref skipHidden, out method);
  1356. return method;
  1357. }
  1358. }
  1359. internal bool TryGetMethod(string/*!*/ name, ref bool skipHidden, out RubyMemberInfo method) {
  1360. return TryGetMethod(name, ref skipHidden, false, out method);
  1361. }
  1362. internal bool TryGetMethod(string/*!*/ name, ref bool skipHidden, bool virtualLookup, out RubyMemberInfo method) {
  1363. Context.RequiresClassHierarchyLock();
  1364. Debug.Assert(_methods != null);
  1365. // lookup Ruby method first:
  1366. if (TryGetDefinedMethod(name, ref skipHidden, out method)) {
  1367. return true;
  1368. }
  1369. if (virtualLookup) {
  1370. string mangled;
  1371. // Note: property and default indexers getters and setters use FooBar, FooBar= and [], []= names, respectively, in virtual sites:
  1372. if ((mangled = RubyUtils.TryMangleMethodName(name)) != null && TryGetDefinedMethod(mangled, ref skipHidden, out method)
  1373. && method.IsRubyMember) {
  1374. return true;
  1375. }
  1376. // Special mappings:
  1377. // Do not map to Kernel#hash/eql?/to_s to prevent recursion in case Object.GetHashCode/Equals/ToString is removed.
  1378. if (this != Context.KernelModule) {
  1379. if (name == "GetHashCode" && TryGetDefinedMethod("hash", out method) && method.IsRubyMember) {
  1380. return true;
  1381. } else if (name == "Equals" && TryGetDefinedMethod("eql?", out method) && method.IsRubyMember) {
  1382. return true;
  1383. } if (name == "ToString" && TryGetDefinedMethod("to_s", out method) && method.IsRubyMember) {
  1384. return true;
  1385. }
  1386. }
  1387. }
  1388. return !skipHidden && TryGetClrMember(name, virtualLookup, out method);
  1389. }
  1390. private bool TryGetClrMember(string/*!*/ name, bool virtualLookup, out RubyMemberInfo method) {
  1391. // Skip hidden CLR overloads.
  1392. // Skip lookup on types that are not visible, that are interfaces or generic type definitions.
  1393. if (_typeTracker != null && !IsModuleType(_typeTracker.Type)) {
  1394. // Note: Do not allow mangling for CLR virtual lookups - we want to match the overridden name exactly as is,
  1395. // so that it corresponds to the base method call the override stub performs.
  1396. bool mapNames = (Restrictions & ModuleRestrictions.NoNameMapping) == 0;
  1397. bool unmangleNames = !virtualLookup && mapNames;
  1398. if (TryGetClrMember(_typeTracker.Type, name, mapNames, unmangleNames, out method)) {
  1399. _methods.Add(name, method);
  1400. return true;
  1401. }
  1402. }
  1403. method = null;
  1404. return false;
  1405. }
  1406. protected virtual bool TryGetClrMember(Type/*!*/ type, string/*!*/ name, bool mapNames, bool unmangleNames, out RubyMemberInfo method) {
  1407. method = null;
  1408. return false;
  1409. }
  1410. public bool EnumerateMethods(Func<RubyModule, string, RubyMemberInfo, bool>/*!*/ action) {
  1411. Context.RequiresClassHierarchyLock();
  1412. InitializeMethodsNoLock();
  1413. foreach (KeyValuePair<string, RubyMemberInfo> method in _methods) {
  1414. // Exclude attached CLR members as they only represent cached CLR method calls and these methods are enumerated below.
  1415. // Include undefined and CLR hidden members - the action uses them to hide the names.
  1416. if (method.Value.IsRubyMember) {
  1417. if (action(this, method.Key, method.Value)) {
  1418. return true;
  1419. }
  1420. }
  1421. }
  1422. // CLR members (do not include interface members - they are not callable methods, just metadata):
  1423. if (_typeTracker != null && !IsModuleType(_typeTracker.Type)) {
  1424. foreach (string name in EnumerateClrMembers(_typeTracker.Type)) {
  1425. if (action(this, name, RubyMemberInfo.InteropMember)) {
  1426. return true;
  1427. }
  1428. }
  1429. }
  1430. return false;
  1431. }
  1432. protected virtual IEnumerable<string/*!*/>/*!*/ EnumerateClrMembers(Type/*!*/ type) {
  1433. return ArrayUtils.EmptyStrings;
  1434. }
  1435. /// <summary>
  1436. /// inherited == false, attributes & attr == Instance:
  1437. /// - get methods in the "self" module
  1438. /// - also include methods on singleton ancestor classes until a non-singleton class is reached
  1439. /// inherited == false, attributes & attr == Singleton:
  1440. /// - get methods only in the "self" module if it's a singleton class
  1441. /// - do not visit mixins nor super classes
  1442. /// inherited == true, attributes & attr == Singleton:
  1443. /// - walk all ancestors until a non-singleton class is reached (do not include non-singleton's methods)
  1444. /// inherited == true, attributes & attr == None:
  1445. /// - walk all ancestors until an Object is reached
  1446. ///
  1447. /// Methods are filtered by visibility specified in attributes (mutliple visibilities could be specified).
  1448. /// A name undefined in a module is not visible in that module and its ancestors.
  1449. /// Method names are not duplicated in the result.
  1450. /// </summary>
  1451. /// <remarks>
  1452. /// Not thread safe.
  1453. /// </remarks>
  1454. public void ForEachMember(bool inherited, RubyMethodAttributes attributes, IEnumerable<string> foreignMembers,
  1455. Action<string/*!*/, RubyModule/*!*/, RubyMemberInfo/*!*/>/*!*/ action) {
  1456. Context.RequiresClassHierarchyLock();
  1457. var visited = new Dictionary<string, RubyMemberInfo>();
  1458. // We can look for instance methods, singleton methods or all methods.
  1459. // The difference is when we stop searching.
  1460. bool instanceMethods = (attributes & RubyMethodAttributes.Instance) != 0;
  1461. bool singletonMethods = (attributes & RubyMethodAttributes.Singleton) != 0;
  1462. // TODO: if we allow creating singletons for foreign objects we need to change this:
  1463. if (foreignMembers != null) {
  1464. foreach (var name in foreignMembers) {
  1465. action(name, this, RubyMethodInfo.InteropMember);
  1466. visited.Add(name, RubyMethodInfo.InteropMember);
  1467. }
  1468. }
  1469. bool stop = false;
  1470. ForEachInstanceMethod(true, delegate(RubyModule/*!*/ module, string name, RubyMemberInfo member) {
  1471. if (member == null) {
  1472. // notification received before any method of the module
  1473. if (stop) {
  1474. return true;
  1475. }
  1476. if (instanceMethods) {
  1477. stop = !inherited && (!IsClass || module.IsClass && !module.IsSingletonClass);
  1478. } else if (singletonMethods) {
  1479. if (!inherited && module != this || module.IsClass && !module.IsSingletonClass) {
  1480. return true;
  1481. }
  1482. } else {
  1483. stop = !inherited;
  1484. }
  1485. } else if (!visited.ContainsKey(name)) {
  1486. // yield the member only if it has the right visibility:
  1487. if (!member.IsUndefined && !member.IsHidden && (((RubyMethodAttributes)member.Visibility & attributes) != 0)) {
  1488. action(name, module, member);
  1489. }
  1490. // visit the member even if it doesn't have the right visibility so that any overridden member with the right visibility
  1491. // won't later be visited:
  1492. visited.Add(name, member);
  1493. }
  1494. return false;
  1495. });
  1496. }
  1497. public void ForEachMember(bool inherited, RubyMethodAttributes attributes, Action<string/*!*/, RubyModule/*!*/, RubyMemberInfo/*!*/>/*!*/ action) {
  1498. ForEachMember(inherited, attributes, null, action);
  1499. }
  1500. #endregion
  1501. #region Class variables (TODO: thread-safety)
  1502. public void ForEachClassVariable(bool inherited, Func<RubyModule, string, object, bool>/*!*/ action) {
  1503. Context.RequiresClassHierarchyLock();
  1504. ForEachAncestor(inherited, delegate(RubyModule/*!*/ module) {
  1505. // notification that we entered the module (it could have no class variable):
  1506. if (action(module, null, Missing.Value)) return true;
  1507. return module.EnumerateClassVariables(action);
  1508. });
  1509. }
  1510. public void SetClassVariable(string/*!*/ name, object value) {
  1511. InitializeClassVariableTable();
  1512. Mutate();
  1513. _classVariables[name] = value;
  1514. }
  1515. public bool TryGetClassVariable(string/*!*/ name, out object value) {
  1516. value = null;
  1517. return _classVariables != null && _classVariables.TryGetValue(name, out value);
  1518. }
  1519. public bool RemoveClassVariable(string/*!*/ name) {
  1520. return _classVariables != null && _classVariables.Remove(name);
  1521. }
  1522. public RubyModule TryResolveClassVariable(string/*!*/ name, out object value) {
  1523. Assert.NotNull(name);
  1524. RubyModule result = null;
  1525. object constValue = null;
  1526. using (Context.ClassHierarchyLocker()) {
  1527. if (ForEachAncestor(delegate(RubyModule/*!*/ module) {
  1528. if (module._classVariables != null && module._classVariables.TryGetValue(name, out constValue)) {
  1529. result = module;
  1530. return true;
  1531. }
  1532. return false;
  1533. })) {
  1534. value = constValue;
  1535. return result;
  1536. }
  1537. }
  1538. value = null;
  1539. return null;
  1540. }
  1541. public bool EnumerateClassVariables(Func<RubyModule, string, object, bool>/*!*/ action) {
  1542. if (_classVariables != null) {
  1543. foreach (KeyValuePair<string, object> variable in _classVariables) {
  1544. if (action(this, variable.Key, variable.Value)) return true;
  1545. }
  1546. }
  1547. return false;
  1548. }
  1549. #endregion
  1550. #region Mixins (thread-safe)
  1551. /// <summary>
  1552. /// Returns true if the CLR type is treated as Ruby module (as opposed to a Ruby class)
  1553. /// </summary>
  1554. public static bool IsModuleType(Type/*!*/ type) {
  1555. return type.IsInterface || type.IsGenericTypeDefinition;
  1556. }
  1557. // thread-safe:
  1558. public bool HasAncestor(RubyModule/*!*/ module) {
  1559. using (Context.ClassHierarchyLocker()) {
  1560. return HasAncestorNoLock(module);
  1561. }
  1562. }
  1563. public bool HasAncestorNoLock(RubyModule/*!*/ module) {
  1564. Context.RequiresClassHierarchyLock();
  1565. return ForEachAncestor(true, (m) => m == module);
  1566. }
  1567. // thread-safe:
  1568. public RubyModule[]/*!*/ GetMixins() {
  1569. using (Context.ClassHierarchyLocker()) {
  1570. return ArrayUtils.Copy(_mixins);
  1571. }
  1572. }
  1573. // thread-safe:
  1574. public void IncludeModules(params RubyModule[]/*!*/ modules) {
  1575. using (Context.ClassHierarchyLocker()) {
  1576. IncludeModulesNoLock(modules);
  1577. }
  1578. }
  1579. internal void IncludeModulesNoLock(RubyModule[]/*!*/ modules) {
  1580. Context.RequiresClassHierarchyLock();
  1581. Mutate();
  1582. RubyUtils.RequireMixins(this, modules);
  1583. RubyModule[] expanded = ExpandMixinsNoLock(GetSuperClass(), _mixins, modules);
  1584. foreach (RubyModule module in expanded) {
  1585. if (module.IsInterface && !CanIncludeClrInterface) {
  1586. if (Array.IndexOf(_mixins, module) == -1) {
  1587. throw new InvalidOperationException(String.Format(
  1588. "Interface `{0}' cannot be included in class `{1}' because its underlying type has already been created",
  1589. module.Name, Name
  1590. ));
  1591. }
  1592. }
  1593. }
  1594. MixinsUpdated(_mixins, _mixins = expanded);
  1595. _context.ConstantAccessVersion++;
  1596. }
  1597. internal void InitializeNewMixin(RubyModule/*!*/ mixin) {
  1598. if (_methodsState != MemberTableState.Uninitialized) {
  1599. mixin.InitializeMethodTableNoLock();
  1600. }
  1601. if (_constantsState != MemberTableState.Uninitialized) {
  1602. mixin.InitializeConstantTableNoLock();
  1603. }
  1604. }
  1605. internal virtual void MixinsUpdated(RubyModule/*!*/[]/*!*/ oldMixins, RubyModule/*!*/[]/*!*/ newMixins) {
  1606. // nop
  1607. }
  1608. // Requires hierarchy lock
  1609. public static RubyModule[]/*!*/ ExpandMixinsNoLock(RubyClass superClass, RubyModule/*!*/[]/*!*/ modules) {
  1610. return ExpandMixinsNoLock(superClass, EmptyArray, modules);
  1611. }
  1612. // Requires hierarchy lock
  1613. private static RubyModule[]/*!*/ ExpandMixinsNoLock(RubyClass superClass, RubyModule/*!*/[]/*!*/ existing, IList<RubyModule/*!*/>/*!*/ added) {
  1614. Assert.NotNull(existing);
  1615. Assert.NotNull(added);
  1616. List<RubyModule> expanded = new List<RubyModule>(existing);
  1617. ExpandMixinsNoLock(superClass, expanded, 0, added, true);
  1618. return expanded.ToArray();
  1619. }
  1620. // Requires hierarchy lock
  1621. private static int ExpandMixinsNoLock(RubyClass superClass, List<RubyModule/*!*/>/*!*/ existing, int index, IList<RubyModule/*!*/>/*!*/ added,
  1622. bool recursive) {
  1623. foreach (RubyModule module in added) {
  1624. Assert.NotNull(module);
  1625. int newIndex = existing.IndexOf(module);
  1626. if (newIndex >= 0) {
  1627. // Module is already present in _mixins
  1628. // Update the insertion point so that we retain ordering of dependencies
  1629. // If we're still in the initial level of recursion, repeat for module's mixins
  1630. index = newIndex + 1;
  1631. if (recursive) {
  1632. index = ExpandMixinsNoLock(superClass, existing, index, module._mixins, false);
  1633. }
  1634. } else {
  1635. // Module is not yet present in _mixins
  1636. // Recursively insert module dependencies at the insertion point, then insert module itself
  1637. newIndex = ExpandMixinsNoLock(superClass, existing, index, module._mixins, false);
  1638. // insert module only if it is not an ancestor of the superclass:
  1639. if (superClass == null || !superClass.HasAncestorNoLock(module)) {
  1640. existing.Insert(index, module);
  1641. index = newIndex + 1;
  1642. } else {
  1643. index = newIndex;
  1644. }
  1645. }
  1646. }
  1647. return index;
  1648. }
  1649. /// <summary>
  1650. /// Returns true if given module is an ancestor of the superclass of this class (provided that this is a class).
  1651. /// </summary>
  1652. internal virtual bool IsSuperClassAncestor(RubyModule/*!*/ module) {
  1653. return false;
  1654. }
  1655. protected virtual bool CanIncludeClrInterface {
  1656. get { return true; }
  1657. }
  1658. internal List<Type> GetImplementedInterfaces() {
  1659. List<Type> interfaces = new List<Type>();
  1660. using (Context.ClassHierarchyLocker()) {
  1661. foreach (RubyModule m in _mixins) {
  1662. if (m.IsInterface && !m.TypeTracker.Type.IsGenericTypeDefinition && !interfaces.Contains(m.TypeTracker.Type)) {
  1663. interfaces.Add(m.TypeTracker.Type);
  1664. }
  1665. }
  1666. }
  1667. return interfaces;
  1668. }
  1669. private void IncludeTraitNoLock(ref Action<RubyModule> initializer, MemberTableState tableState, Action<RubyModule>/*!*/ trait) {
  1670. Assert.NotNull(trait);
  1671. if (tableState == MemberTableState.Uninitialized) {
  1672. if (initializer != null && initializer != EmptyInitializer) {
  1673. initializer += trait;
  1674. } else {
  1675. initializer = trait;
  1676. }
  1677. } else {
  1678. // TODO: postpone? hold lock?
  1679. using (Context.ClassHierarchyUnlocker()) {
  1680. trait(this);
  1681. }
  1682. }
  1683. }
  1684. internal void IncludeLibraryModule(Action<RubyModule> instanceTrait, Action<RubyModule> classTrait, Action<RubyModule> constantsInitializer,
  1685. RubyModule/*!*/[]/*!*/ mixins, bool builtin) {
  1686. // Do not allow non-builtin library inclusions to a frozen module.
  1687. // We need to ensure that initializers are not modified once a module is frozen.
  1688. if (!builtin) {
  1689. Mutate();
  1690. }
  1691. using (Context.ClassHierarchyLocker()) {
  1692. if (instanceTrait != null) {
  1693. IncludeTraitNoLock(ref _methodsInitializer, _methodsState, instanceTrait);
  1694. }
  1695. if (constantsInitializer != null) {
  1696. IncludeTraitNoLock(ref _constantsInitializer, _constantsState, constantsInitializer);
  1697. }
  1698. if (classTrait != null) {
  1699. var singleton = GetOrCreateSingletonClass();
  1700. singleton.IncludeTraitNoLock(ref singleton._methodsInitializer, singleton._methodsState, classTrait);
  1701. }
  1702. // updates the module version:
  1703. IncludeModulesNoLock(mixins);
  1704. }
  1705. }
  1706. #endregion
  1707. #region Names
  1708. public string GetName(RubyContext/*!*/ context) {
  1709. return context == _context ? _name : _name + "@" + _context.RuntimeId;
  1710. }
  1711. public MutableString GetDisplayName(RubyContext/*!*/ context, bool showEmptyName) {
  1712. if (IsSingletonClass) {
  1713. RubyClass c = (RubyClass)this;
  1714. object singletonOf;
  1715. MutableString result = MutableString.CreateMutable(context.GetIdentifierEncoding());
  1716. int nestings = 0;
  1717. while (true) {
  1718. nestings++;
  1719. result.Append("#<Class:");
  1720. singletonOf = c.SingletonClassOf;
  1721. RubyModule module = singletonOf as RubyModule;
  1722. if (module == null || !module.IsSingletonClass && module.Name == null) {
  1723. nestings++;
  1724. result.Append("#<");
  1725. result.Append(c.SuperClass.GetName(context));
  1726. result.Append(':');
  1727. RubyUtils.AppendFormatHexObjectId(result, RubyUtils.GetObjectId(_context, singletonOf));
  1728. break;
  1729. }
  1730. if (!module.IsSingletonClass) {
  1731. result.Append(module.GetName(context));
  1732. break;
  1733. }
  1734. c = (RubyClass)module;
  1735. }
  1736. return result.Append('>', nestings);
  1737. } else if (_name == null) {
  1738. if (showEmptyName) {
  1739. return null;
  1740. } else {
  1741. MutableString result = MutableString.CreateMutable(context.GetIdentifierEncoding());
  1742. result.Append("#<");
  1743. result.Append(_context.GetClassOf(this).GetName(context));
  1744. result.Append(':');
  1745. RubyUtils.AppendFormatHexObjectId(result, RubyUtils.GetObjectId(_context, this));
  1746. result.Append('>');
  1747. return result;
  1748. }
  1749. } else {
  1750. return MutableString.CreateMutable(GetName(context), context.GetIdentifierEncoding());
  1751. }
  1752. }
  1753. #endregion
  1754. #region Debug View
  1755. // see: RubyObject.DebuggerDisplayValue
  1756. internal string/*!*/ GetDebuggerDisplayValue(object obj) {
  1757. return Context.Inspect(obj).ConvertToString();
  1758. }
  1759. // see: RubyObject.DebuggerDisplayType
  1760. internal string/*!*/ GetDebuggerDisplayType() {
  1761. return Name;
  1762. }
  1763. internal sealed class DebugView {
  1764. private readonly RubyModule/*!*/ _obj;
  1765. public DebugView(RubyModule/*!*/ obj) {
  1766. Assert.NotNull(obj);
  1767. _obj = obj;
  1768. }
  1769. #region RubyObjectDebugView
  1770. [DebuggerDisplay("{GetModuleName(A),nq}", Name = "{GetClassKind(),nq}", Type = "")]
  1771. public object A {
  1772. get { return _obj.ImmediateClass; }
  1773. }
  1774. [DebuggerDisplay("{B}", Name = "tainted?", Type = "")]
  1775. public bool B {
  1776. get { return _obj.IsTainted; }
  1777. set { _obj.IsTainted = value; }
  1778. }
  1779. [DebuggerDisplay("{C}", Name = "untrusted?", Type = "")]
  1780. public bool C {
  1781. get { return _obj.IsUntrusted; }
  1782. set { _obj.IsUntrusted = value; }
  1783. }
  1784. [DebuggerDisplay("{D}", Name = "frozen?", Type = "")]
  1785. public bool D {
  1786. get { return _obj.IsFrozen; }
  1787. set { if (value) { _obj.Freeze(); } }
  1788. }
  1789. [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
  1790. public object E {
  1791. get {
  1792. var instanceData = _obj.TryGetInstanceData();
  1793. if (instanceData == null) {
  1794. return new RubyInstanceData.VariableDebugView[0];
  1795. }
  1796. return instanceData.GetInstanceVariablesDebugView(_obj.ImmediateClass.Context);
  1797. }
  1798. }
  1799. private string GetClassKind() {
  1800. return _obj.ImmediateClass.IsSingletonClass ? "singleton class" : "class";
  1801. }
  1802. private static string GetModuleName(object module) {
  1803. var m = (RubyModule)module;
  1804. return m != null ? m.GetDisplayName(m.Context, false).ToString() : null;
  1805. }
  1806. #endregion
  1807. [DebuggerDisplay("{GetModuleName(F),nq}", Name = "super", Type = "")]
  1808. public object F {
  1809. get { return _obj.GetSuperClass(); }
  1810. }
  1811. [DebuggerDisplay("", Name = "mixins", Type = "")]
  1812. public object G {
  1813. get { return _obj.GetMixins(); }
  1814. }
  1815. [DebuggerDisplay("", Name = "instance methods", Type = "")]
  1816. public object H {
  1817. get { return GetMethods(RubyMethodAttributes.Instance); }
  1818. }
  1819. [DebuggerDisplay("", Name = "singleton methods", Type = "")]
  1820. public object I {
  1821. get { return GetMethods(RubyMethodAttributes.Singleton); }
  1822. }
  1823. private Dictionary<string, RubyMemberInfo> GetMethods(RubyMethodAttributes attributes) {
  1824. // TODO: custom view for methods, sorted
  1825. var result = new Dictionary<string, RubyMemberInfo>();
  1826. using (_obj.Context.ClassHierarchyLocker()) {
  1827. _obj.ForEachMember(false, attributes | RubyMethodAttributes.VisibilityMask, (name, _, info) => {
  1828. result[name] = info;
  1829. });
  1830. }
  1831. return result;
  1832. }
  1833. // TODO: class variables
  1834. }
  1835. #endregion
  1836. }
  1837. }