PageRenderTime 64ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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 creat…

Large files files are truncated, but you can click here to view the full file