PageRenderTime 63ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/Ruby/Ruby/Runtime/RubyContext.cs

http://github.com/IronLanguages/main
C# | 3085 lines | 2196 code | 574 blank | 315 comment | 400 complexity | 76f7b2770cb8793948b287af4862ca4f 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. #if FEATURE_CORE_DLR
  16. using MSA = System.Linq.Expressions;
  17. #else
  18. using MSA = Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Dynamic;
  24. using System.IO;
  25. using System.Reflection;
  26. using System.Runtime.CompilerServices;
  27. using System.Security;
  28. using System.Text;
  29. using System.Threading;
  30. using IronRuby.Builtins;
  31. using IronRuby.Compiler;
  32. using IronRuby.Compiler.Ast;
  33. using IronRuby.Compiler.Generation;
  34. using IronRuby.Hosting;
  35. using IronRuby.Runtime.Calls;
  36. using IronRuby.Runtime.Conversions;
  37. using Microsoft.Scripting;
  38. using Microsoft.Scripting.Actions;
  39. using Microsoft.Scripting.Runtime;
  40. using Microsoft.Scripting.Utils;
  41. using System.Collections.ObjectModel;
  42. using System.Globalization;
  43. namespace IronRuby.Runtime {
  44. [ReflectionCached]
  45. public sealed class RubyContext : LanguageContext {
  46. #region Constants
  47. internal static readonly Guid RubyLanguageGuid = new Guid("F03C4640-DABA-473f-96F1-391400714DAB");
  48. private static readonly Guid LanguageVendor_Microsoft = new Guid(-1723120188, -6423, 0x11d2, 0x90, 0x3f, 0, 0xc0, 0x4f, 0xa3, 2, 0xa1);
  49. private static int _RuntimeIdGenerator = 0;
  50. // MRI compliance:
  51. public string/*!*/ MriVersion {
  52. get { return "1.9.2"; }
  53. }
  54. public string/*!*/ StandardLibraryVersion {
  55. get { return "1.9.1"; }
  56. }
  57. public string/*!*/ MriReleaseDate {
  58. get { return "2010-08-18"; }
  59. }
  60. public int MriPatchLevel {
  61. get { return 0; }
  62. }
  63. public const string BinDirEnvironmentVariable = "IRONRUBY_11";
  64. // IronRuby:
  65. public const string IronRubyInformationalVersion = "1.1.3";
  66. #if !SILVERLIGHT
  67. public const string/*!*/ IronRubyVersionString = "1.1.3.0";
  68. public static readonly Version IronRubyVersion = new Version(1, 1, 3, 0);
  69. #else
  70. public const string/*!*/ IronRubyVersionString = "1.1.1302.0";
  71. public static readonly Version IronRubyVersion = new Version(1, 1, 1302, 0);
  72. #endif
  73. internal const string/*!*/ IronRubyDisplayName = "IronRuby";
  74. internal const string/*!*/ IronRubyNames = "IronRuby;Ruby;rb";
  75. internal const string/*!*/ IronRubyFileExtensions = ".rb";
  76. #endregion
  77. // TODO: remove
  78. internal static RubyContext _Default;
  79. private readonly int _runtimeId;
  80. private readonly RubyScope/*!*/ _emptyScope;
  81. private RubyOptions/*!*/ _options;
  82. private readonly TopNamespaceTracker _namespaces;
  83. private readonly Loader/*!*/ _loader;
  84. private readonly Scope/*!*/ _globalScope;
  85. private readonly RubyMetaBinderFactory/*!*/ _metaBinderFactory;
  86. private readonly RubyBinder _binder;
  87. private DynamicDelegateCreator _delegateCreator;
  88. private RubyService _rubyService;
  89. #region Global Variables (thread-safe access)
  90. /// <summary>
  91. /// $0
  92. /// </summary>
  93. public MutableString CommandLineProgramPath { get; set; }
  94. /// <summary>
  95. /// $? of type Process::Status
  96. /// </summary>
  97. [ThreadStatic]
  98. private static object _childProcessExitStatus;
  99. /// <summary>
  100. /// $/, $-O
  101. /// </summary>
  102. private MutableString _inputSeparator;
  103. /// <summary>
  104. /// $\
  105. /// </summary>
  106. private MutableString _outputSeparator;
  107. /// <summary>
  108. /// $;, $-F
  109. /// </summary>
  110. private object _stringSeparator;
  111. /// <summary>
  112. /// $,
  113. /// </summary>
  114. private MutableString _itemSeparator;
  115. private readonly Dictionary<string/*!*/, GlobalVariable>/*!*/ _globalVariables;
  116. public object/*!*/ GlobalVariablesLock { get { return _globalVariables; } }
  117. // not thread safe: use GlobalVariablesLock to synchronize access to the variables:
  118. public IEnumerable<KeyValuePair<string, GlobalVariable>>/*!*/ GlobalVariables {
  119. get { return _globalVariables; }
  120. }
  121. #endregion
  122. #region Random Number Generator
  123. private readonly object _randomNumberGeneratorLock = new object();
  124. private Random _randomNumberGenerator; // lazy
  125. private object _randomNumberGeneratorSeed = ScriptingRuntimeHelpers.Int32ToObject(0);
  126. public object RandomNumberGeneratorSeed {
  127. get { return _randomNumberGeneratorSeed; }
  128. }
  129. public void SeedRandomNumberGenerator(IntegerValue value) {
  130. lock (_randomNumberGeneratorLock) {
  131. _randomNumberGenerator = new Random(value.IsFixnum ? value.Fixnum : value.Bignum.GetHashCode());
  132. _randomNumberGeneratorSeed = value.ToObject();
  133. }
  134. }
  135. public Random/*!*/ RandomNumberGenerator {
  136. get {
  137. if (_randomNumberGenerator == null) {
  138. lock (_randomNumberGeneratorLock) {
  139. if (_randomNumberGenerator == null) {
  140. _randomNumberGenerator = new Random();
  141. }
  142. }
  143. }
  144. return _randomNumberGenerator;
  145. }
  146. }
  147. #endregion
  148. #region Threading
  149. // Thread#main
  150. private readonly Thread _mainThread;
  151. // Thread#critical=
  152. // We just need a bool. But we store the Thread object for easier debugging if there is a hang
  153. private Thread _criticalThread;
  154. private readonly object _criticalMonitor = new object();
  155. #endregion
  156. #region Tracing
  157. private readonly RubyInputProvider/*!*/ _inputProvider;
  158. private Proc _traceListener;
  159. [ThreadStatic]
  160. private bool _traceListenerSuspended;
  161. private readonly Stopwatch _upTime;
  162. // TODO: thread-safety
  163. internal Action<Expression, MSA.DynamicExpression> CallSiteCreated { get; set; }
  164. #endregion
  165. #region Look-aside tables
  166. /// <summary>
  167. /// Maps CLR types to Ruby classes/modules.
  168. /// Doesn't contain classes defined in Ruby.
  169. /// </summary>
  170. private readonly Dictionary<Type, RubyModule>/*!*/ _moduleCache;
  171. private object ModuleCacheLock { get { return _moduleCache; } }
  172. /// <summary>
  173. /// Maps CLR namespace trackers to Ruby modules.
  174. /// </summary>
  175. private readonly Dictionary<NamespaceTracker, RubyModule>/*!*/ _namespaceCache;
  176. private object NamespaceCacheLock { get { return _namespaceCache; } }
  177. // Maps objects to InstanceData. The keys store weak references to the objects.
  178. // Objects are compared by reference (identity).
  179. // An entry can be removed as soon as the key object becomes unreachable.
  180. private readonly WeakTable<object, RubyInstanceData>/*!*/ _referenceTypeInstanceData;
  181. private object/*!*/ ReferenceTypeInstanceDataLock { get { return _referenceTypeInstanceData; } }
  182. // Maps values to InstanceData. The keys store value representatives.
  183. // All objects that has the same value (value-equality) map to the same InstanceData.
  184. // Entries cannot be ever freed since anytime in future one may create a new object whose value has already been mapped to InstanceData.
  185. private readonly Dictionary<object, RubyInstanceData>/*!*/ _valueTypeInstanceData;
  186. private object/*!*/ ValueTypeInstanceDataLock { get { return _valueTypeInstanceData; } }
  187. // not thread-safe:
  188. private readonly RubyInstanceData/*!*/ _nilInstanceData = new RubyInstanceData(RubyUtils.NilObjectId);
  189. #endregion
  190. #region Class Hierarchy
  191. public IDisposable/*!*/ ClassHierarchyLocker() {
  192. return _classHierarchyLock.CreateLocker();
  193. }
  194. public IDisposable/*!*/ ClassHierarchyUnlocker() {
  195. return _classHierarchyLock.CreateUnlocker();
  196. }
  197. private readonly CheckedMonitor/*!*/ _classHierarchyLock = new CheckedMonitor();
  198. [Emitted]
  199. public int ConstantAccessVersion = 1;
  200. [Conditional("DEBUG")]
  201. internal void RequiresClassHierarchyLock() {
  202. Debug.Assert(_classHierarchyLock.IsLocked, "Code can only be executed while holding class hierarchy lock.");
  203. }
  204. // classes used by runtime (we need to update initialization generator if any of these are added):
  205. private RubyClass/*!*/ _basicObjectClass;
  206. private RubyModule/*!*/ _kernelModule;
  207. private RubyClass/*!*/ _objectClass;
  208. private RubyClass/*!*/ _classClass;
  209. private RubyClass/*!*/ _moduleClass;
  210. private RubyClass/*!*/ _nilClass;
  211. private RubyClass/*!*/ _trueClass;
  212. private RubyClass/*!*/ _falseClass;
  213. private RubyClass/*!*/ _exceptionClass;
  214. private RubyClass _standardErrorClass;
  215. private RubyClass _comObjectClass;
  216. private Action<RubyModule>/*!*/ _mainSingletonTrait;
  217. // internally set by Initializer:
  218. public RubyClass/*!*/ BasicObjectClass { get { return _basicObjectClass; } }
  219. public RubyModule/*!*/ KernelModule { get { return _kernelModule; } }
  220. public RubyClass/*!*/ ObjectClass { get { return _objectClass; } }
  221. public RubyClass/*!*/ ClassClass { get { return _classClass; } set { _classClass = value; } }
  222. public RubyClass/*!*/ ModuleClass { get { return _moduleClass; } set { _moduleClass = value; } }
  223. public RubyClass/*!*/ NilClass { get { return _nilClass; } set { _nilClass = value; } }
  224. public RubyClass/*!*/ TrueClass { get { return _trueClass; } set { _trueClass = value; } }
  225. public RubyClass/*!*/ FalseClass { get { return _falseClass; } set { _falseClass = value; } }
  226. public RubyClass ExceptionClass { get { return _exceptionClass; } set { _exceptionClass = value; } }
  227. public RubyClass StandardErrorClass { get { return _standardErrorClass; } set { _standardErrorClass = value; } }
  228. internal RubyClass ComObjectClass {
  229. get {
  230. #if !SILVERLIGHT // COM
  231. if (_comObjectClass == null) {
  232. GetOrCreateClass(TypeUtils.ComObjectType);
  233. }
  234. #endif
  235. return _comObjectClass;
  236. }
  237. }
  238. // Set of names that method_missing defined on any module was resolved for and that are cached. Lazy init.
  239. //
  240. // Note: We used to have this set per module but that doesn't work - see unit test MethodCallCaching_MethodMissing4.
  241. // Whenever a method is added to a class C we would need to traverse all its subclasses to see if any of them
  242. // has the added method in its MissingMethodsCachedInSites table.
  243. // TODO: Could we optimize this search? If so we could also free the per-module set if mm is removed.
  244. internal HashSet<string> MissingMethodsCachedInSites { get; set; }
  245. #endregion
  246. #region Properties
  247. public PlatformAdaptationLayer/*!*/ Platform {
  248. get { return DomainManager.Platform; }
  249. }
  250. public override LanguageOptions Options {
  251. get { return _options; }
  252. }
  253. public RubyOptions RubyOptions {
  254. get { return _options; }
  255. }
  256. internal RubyScope/*!*/ EmptyScope {
  257. get { return _emptyScope; }
  258. }
  259. public Thread MainThread {
  260. get { return _mainThread; }
  261. }
  262. public MutableString InputSeparator {
  263. get { return _inputSeparator; }
  264. set { _inputSeparator = value; }
  265. }
  266. public MutableString OutputSeparator {
  267. get { return _outputSeparator; }
  268. set { _outputSeparator = value; }
  269. }
  270. public object StringSeparator {
  271. get { return _stringSeparator; }
  272. set { _stringSeparator = value; }
  273. }
  274. public MutableString ItemSeparator {
  275. get { return _itemSeparator; }
  276. set { _itemSeparator = value; }
  277. }
  278. public object CriticalMonitor {
  279. get { return _criticalMonitor; }
  280. }
  281. public Thread CriticalThread {
  282. get { return _criticalThread; }
  283. set { _criticalThread = value; }
  284. }
  285. public Proc TraceListener {
  286. get { return _traceListener; }
  287. set { _traceListener = value; }
  288. }
  289. public RubyInputProvider/*!*/ InputProvider {
  290. get { return _inputProvider; }
  291. }
  292. public object ChildProcessExitStatus {
  293. get { return _childProcessExitStatus; }
  294. set { _childProcessExitStatus = value; }
  295. }
  296. public Scope/*!*/ TopGlobalScope {
  297. get { return _globalScope; }
  298. }
  299. internal RubyMetaBinderFactory/*!*/ MetaBinderFactory {
  300. get { return _metaBinderFactory; }
  301. }
  302. public Loader/*!*/ Loader {
  303. get { return _loader; }
  304. }
  305. public bool ShowCls {
  306. get { return false; }
  307. }
  308. public EqualityComparer/*!*/ EqualityComparer {
  309. get {
  310. if (_equalityComparer == null) {
  311. _equalityComparer = new EqualityComparer(this);
  312. }
  313. return _equalityComparer;
  314. }
  315. }
  316. private EqualityComparer _equalityComparer;
  317. public override Version LanguageVersion {
  318. get { return IronRubyVersion; }
  319. }
  320. public override Guid LanguageGuid {
  321. get { return RubyLanguageGuid; }
  322. }
  323. public override Guid VendorGuid {
  324. get { return LanguageVendor_Microsoft; }
  325. }
  326. public int RuntimeId {
  327. get { return _runtimeId; }
  328. }
  329. internal TopNamespaceTracker/*!*/ Namespaces {
  330. get { return _namespaces; }
  331. }
  332. public object Verbose { get; set; }
  333. private RubyEncoding/*!*/ _defaultExternalEncoding;
  334. public RubyEncoding/*!*/ DefaultExternalEncoding {
  335. get { return _defaultExternalEncoding; }
  336. set {
  337. ContractUtils.RequiresNotNull(value, "value");
  338. _defaultExternalEncoding = value;
  339. }
  340. }
  341. public RubyEncoding DefaultInternalEncoding { get; set; }
  342. #endregion
  343. #region Initialization
  344. public RubyContext(ScriptDomainManager/*!*/ manager, IDictionary<string, object> options)
  345. : base(manager) {
  346. ContractUtils.RequiresNotNull(manager, "manager");
  347. _options = new RubyOptions(options);
  348. _runtimeId = Interlocked.Increment(ref _RuntimeIdGenerator);
  349. _upTime = new Stopwatch();
  350. _upTime.Start();
  351. _binder = new RubyBinder(this);
  352. _symbols = new Dictionary<MutableString, RubySymbol>();
  353. _metaBinderFactory = new RubyMetaBinderFactory(this);
  354. _runtimeErrorSink = new RuntimeErrorSink(this);
  355. _equalityComparer = new EqualityComparer(this);
  356. _globalVariables = new Dictionary<string, GlobalVariable>();
  357. _moduleCache = new Dictionary<Type, RubyModule>();
  358. _namespaceCache = new Dictionary<NamespaceTracker, RubyModule>();
  359. _referenceTypeInstanceData = new WeakTable<object, RubyInstanceData>();
  360. _valueTypeInstanceData = new Dictionary<object, RubyInstanceData>();
  361. _inputProvider = new RubyInputProvider(this, _options.Arguments, _options.LocaleEncoding);
  362. _defaultExternalEncoding = _options.DefaultEncoding ?? _options.LocaleEncoding;
  363. _globalScope = DomainManager.Globals;
  364. _loader = new Loader(this);
  365. _emptyScope = new RubyTopLevelScope(this);
  366. _currentException = null;
  367. _currentSafeLevel = 0;
  368. _childProcessExitStatus = null;
  369. _inputSeparator = MutableString.CreateAscii("\n");
  370. _outputSeparator = null;
  371. _stringSeparator = null;
  372. _itemSeparator = null;
  373. _mainThread = Thread.CurrentThread;
  374. if (_options.MainFile != null) {
  375. CommandLineProgramPath = EncodePath(_options.MainFile);
  376. }
  377. if (_options.Verbosity <= 0) {
  378. Verbose = null;
  379. } else if (_options.Verbosity == 1) {
  380. Verbose = ScriptingRuntimeHelpers.False;
  381. } else {
  382. Verbose = ScriptingRuntimeHelpers.True;
  383. }
  384. _namespaces = new TopNamespaceTracker(manager);
  385. manager.AssemblyLoaded += new EventHandler<AssemblyLoadedEventArgs>((_, e) => AssemblyLoaded(e.Assembly));
  386. foreach (Assembly asm in manager.GetLoadedAssemblyList()) {
  387. AssemblyLoaded(asm);
  388. }
  389. // TODO:
  390. Interlocked.CompareExchange(ref _Default, this, null);
  391. _loader.LoadBuiltins();
  392. Debug.Assert(_exceptionClass != null && _standardErrorClass != null && _nilClass != null);
  393. Debug.Assert(_classClass != null && _moduleClass != null);
  394. // needs to run before globals and constants are initialized:
  395. InitializeFileDescriptors(DomainManager.SharedIO);
  396. InitializeGlobalConstants();
  397. InitializeGlobalVariables();
  398. }
  399. internal RubyBinder/*!*/ Binder {
  400. get { return _binder; }
  401. }
  402. /// <summary>
  403. /// Clears thread static variables.
  404. /// </summary>
  405. internal static void ClearThreadStatics() {
  406. _currentException = null;
  407. }
  408. private void InitializeGlobalVariables() {
  409. // special variables:
  410. Runtime.GlobalVariables.DefineVariablesNoLock(this);
  411. // TODO:
  412. // $-a
  413. // $F
  414. // $-i
  415. // $-l
  416. // $-p
  417. // $?
  418. // $0
  419. DefineGlobalVariableNoLock("PROGRAM_NAME", Runtime.GlobalVariables.CommandLineProgramPath);
  420. DefineGlobalVariableNoLock("stdin", Runtime.GlobalVariables.InputStream);
  421. DefineGlobalVariableNoLock("stdout", Runtime.GlobalVariables.OutputStream);
  422. DefineGlobalVariableNoLock("defout", Runtime.GlobalVariables.OutputStream);
  423. DefineGlobalVariableNoLock("stderr", Runtime.GlobalVariables.ErrorOutputStream);
  424. DefineGlobalVariableNoLock("LOADED_FEATURES", Runtime.GlobalVariables.LoadedFiles);
  425. DefineGlobalVariableNoLock("LOAD_PATH", Runtime.GlobalVariables.LoadPath);
  426. DefineGlobalVariableNoLock("-I", Runtime.GlobalVariables.LoadPath);
  427. DefineGlobalVariableNoLock("-O", Runtime.GlobalVariables.InputSeparator);
  428. DefineGlobalVariableNoLock("-F", Runtime.GlobalVariables.StringSeparator);
  429. DefineGlobalVariableNoLock("FILENAME", Runtime.GlobalVariables.InputFileName);
  430. GlobalVariableInfo debug = new GlobalVariableInfo(DomainManager.Configuration.DebugMode || RubyOptions.DebugVariable);
  431. DefineGlobalVariableNoLock("VERBOSE", Runtime.GlobalVariables.Verbose);
  432. DefineGlobalVariableNoLock("-v", Runtime.GlobalVariables.Verbose);
  433. DefineGlobalVariableNoLock("-w", Runtime.GlobalVariables.Verbose);
  434. DefineGlobalVariableNoLock("DEBUG", debug);
  435. DefineGlobalVariableNoLock("-d", debug);
  436. DefineGlobalVariableNoLock("KCODE", Runtime.GlobalVariables.KCode);
  437. DefineGlobalVariableNoLock("-K", Runtime.GlobalVariables.KCode);
  438. #if !SILVERLIGHT
  439. DefineGlobalVariableNoLock("SAFE", Runtime.GlobalVariables.SafeLevel);
  440. try {
  441. TrySetCurrentProcessVariables();
  442. } catch (SecurityException) {
  443. // nop
  444. }
  445. #endif
  446. }
  447. #if !SILVERLIGHT // process
  448. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  449. private void TrySetCurrentProcessVariables() {
  450. Process process = Process.GetCurrentProcess();
  451. DefineGlobalVariableNoLock(Symbols.CurrentProcessId, new ReadOnlyGlobalVariableInfo(process.Id));
  452. }
  453. #endif
  454. private void InitializeGlobalConstants() {
  455. Debug.Assert(_objectClass != null);
  456. MutableString version = MutableString.CreateAscii(MriVersion);
  457. MutableString platform = MakePlatformString();
  458. MutableString releaseDate = MutableString.CreateAscii(MriReleaseDate);
  459. MutableString rubyEngine = MutableString.CreateAscii("ironruby");
  460. using (ClassHierarchyLocker()) {
  461. RubyClass obj = _objectClass;
  462. obj.SetConstantNoMutateNoLock("RUBY_ENGINE", rubyEngine);
  463. obj.SetConstantNoMutateNoLock("RUBY_VERSION", version);
  464. obj.SetConstantNoMutateNoLock("RUBY_PATCHLEVEL", MriPatchLevel);
  465. obj.SetConstantNoMutateNoLock("RUBY_PLATFORM", platform);
  466. obj.SetConstantNoMutateNoLock("RUBY_RELEASE_DATE", releaseDate);
  467. obj.SetConstantNoMutateNoLock("RUBY_DESCRIPTION", MutableString.CreateAscii(MakeDescriptionString()));
  468. obj.SetConstantNoMutateNoLock("VERSION", version);
  469. obj.SetConstantNoMutateNoLock("PLATFORM", platform);
  470. obj.SetConstantNoMutateNoLock("RELEASE_DATE", releaseDate);
  471. obj.SetConstantNoMutateNoLock("IRONRUBY_VERSION", MutableString.CreateAscii(RubyContext.IronRubyVersionString));
  472. obj.SetConstantNoMutateNoLock("STDIN", StandardInput);
  473. obj.SetConstantNoMutateNoLock("STDOUT", StandardOutput);
  474. obj.SetConstantNoMutateNoLock("STDERR", StandardErrorOutput);
  475. ConstantStorage argf;
  476. if (obj.TryGetConstantNoAutoloadCheck("ARGF", out argf)) {
  477. _inputProvider.Singleton = argf.Value;
  478. }
  479. obj.SetConstantNoMutateNoLock("ARGV", _inputProvider.CommandLineArguments);
  480. // Hash
  481. // SCRIPT_LINES__
  482. }
  483. }
  484. public static string/*!*/ MakeDescriptionString() {
  485. return String.Format(CultureInfo.InvariantCulture, "IronRuby {0} on {1}", IronRubyVersion, MakeRuntimeDesriptionString());
  486. }
  487. internal static string MakeRuntimeDesriptionString() {
  488. Type mono = typeof(object).Assembly.GetType("Mono.Runtime");
  489. return mono != null ?
  490. (string)mono.GetMethod("GetDisplayName", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null)
  491. : String.Format(CultureInfo.InvariantCulture, ".NET {0}", Environment.Version);
  492. }
  493. private static MutableString/*!*/ MakePlatformString() {
  494. switch (Environment.OSVersion.Platform) {
  495. case PlatformID.MacOSX:
  496. return MutableString.CreateAscii("i386-darwin");
  497. case PlatformID.Unix:
  498. return MutableString.CreateAscii("i386-linux");
  499. case PlatformID.Win32NT:
  500. case PlatformID.Win32S:
  501. case PlatformID.Win32Windows:
  502. return MutableString.CreateAscii("i386-mswin32");
  503. default:
  504. return MutableString.CreateAscii("unknown");
  505. }
  506. }
  507. private void InitializeFileDescriptors(SharedIO/*!*/ io) {
  508. Debug.Assert(_fileDescriptors.Count == 0);
  509. Stream stream = new ConsoleStream(io, ConsoleStreamType.Input);
  510. StandardInput = new RubyIO(this, stream, AllocateFileDescriptor(stream), IOMode.ReadOnly);
  511. stream = new ConsoleStream(io, ConsoleStreamType.Output);
  512. StandardOutput = new RubyIO(this, stream, AllocateFileDescriptor(stream), IOMode.WriteOnly | IOMode.WriteAppends);
  513. stream = new ConsoleStream(io, ConsoleStreamType.ErrorOutput);
  514. StandardErrorOutput = new RubyIO(this, stream, AllocateFileDescriptor(stream), IOMode.WriteOnly | IOMode.WriteAppends);
  515. }
  516. // TODO: internal
  517. public void RegisterPrimitives(
  518. Action<RubyModule>/*!*/ mainSingletonTrait,
  519. Action<RubyModule>/*!*/ basicObjectInstanceTrait,
  520. Action<RubyModule>/*!*/ basicObjectClassTrait,
  521. Action<RubyModule> basicObjectConstantsInitializer,
  522. Action<RubyModule>/*!*/ kernelInstanceTrait,
  523. Action<RubyModule>/*!*/ kernelClassTrait,
  524. Action<RubyModule> kernelConstantsInitializer,
  525. Action<RubyModule>/*!*/ objectInstanceTrait,
  526. Action<RubyModule>/*!*/ objectClassTrait,
  527. Action<RubyModule> objectConstantsInitializer,
  528. Action<RubyModule>/*!*/ moduleInstanceTrait,
  529. Action<RubyModule>/*!*/ moduleClassTrait,
  530. Action<RubyModule> moduleConstantsInitializer,
  531. Action<RubyModule>/*!*/ classInstanceTrait,
  532. Action<RubyModule>/*!*/ classClassTrait,
  533. Action<RubyModule> classConstantsInitializer) {
  534. Assert.NotNull(mainSingletonTrait, basicObjectInstanceTrait, basicObjectClassTrait);
  535. Assert.NotNull(objectInstanceTrait, kernelInstanceTrait, moduleInstanceTrait, classInstanceTrait);
  536. Assert.NotNull(objectClassTrait, kernelClassTrait, moduleClassTrait, classClassTrait);
  537. _mainSingletonTrait = mainSingletonTrait;
  538. // inheritance hierarchy:
  539. //
  540. // Class
  541. // ^
  542. // BasicObject -> BasicObject'
  543. // ^ ^
  544. // Object -> Object'
  545. // ^ ^
  546. // Module -> Module'
  547. // ^ ^
  548. // Class -> Class'
  549. // ^
  550. // Object'
  551. //
  552. // only Object should expose CLR methods:
  553. TypeTracker objectTracker = ReflectionCache.GetTypeTracker(typeof(object));
  554. var moduleFactories = new Delegate[] {
  555. new Func<RubyScope, BlockParam, RubyClass, object>(RubyModule.CreateAnonymousModule),
  556. };
  557. var classFactories = new Delegate[] {
  558. new Func<RubyScope, BlockParam, RubyClass, RubyClass, object>(RubyClass.CreateAnonymousClass),
  559. };
  560. // locks to comply with lock requirements:
  561. using (ClassHierarchyLocker()) {
  562. _basicObjectClass = new RubyClass(this, Symbols.BasicObject, null, null, basicObjectInstanceTrait, basicObjectConstantsInitializer, null, null, null, null, null, false, false, ModuleRestrictions.Builtin & ~ModuleRestrictions.NoOverrides);
  563. _kernelModule = new RubyModule(this, Symbols.Kernel, kernelInstanceTrait, kernelConstantsInitializer, null, null, null, ModuleRestrictions.Builtin);
  564. _objectClass = new RubyClass(this, Symbols.Object, objectTracker.Type, null, objectInstanceTrait, objectConstantsInitializer, null, _basicObjectClass, new[] { _kernelModule }, objectTracker, null, false, false, ModuleRestrictions.Builtin & ~ModuleRestrictions.NoOverrides);
  565. _moduleClass = new RubyClass(this, Symbols.Module, typeof(RubyModule), null, moduleInstanceTrait, moduleConstantsInitializer, moduleFactories, _objectClass, null, null, null, false, false, ModuleRestrictions.Builtin);
  566. _classClass = new RubyClass(this, Symbols.Class, typeof(RubyClass), null, classInstanceTrait, classConstantsInitializer, classFactories, _moduleClass, null, null, null, false, false, ModuleRestrictions.Builtin);
  567. _basicObjectClass.InitializeImmediateClass(_basicObjectClass.CreateSingletonClass(_classClass, basicObjectClassTrait));
  568. _objectClass.InitializeImmediateClass(_objectClass.CreateSingletonClass(_basicObjectClass.ImmediateClass, objectClassTrait));
  569. _moduleClass.InitializeImmediateClass(_moduleClass.CreateSingletonClass(_objectClass.ImmediateClass, moduleClassTrait));
  570. _classClass.InitializeImmediateClass(_classClass.CreateSingletonClass(_moduleClass.ImmediateClass, classClassTrait));
  571. _moduleClass.InitializeDummySingleton();
  572. _classClass.InitializeDummySingleton();
  573. _basicObjectClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass());
  574. _objectClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass());
  575. _moduleClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass());
  576. _classClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass());
  577. _kernelModule.InitializeImmediateClass(_moduleClass, kernelClassTrait);
  578. _objectClass.SetConstantNoMutateNoLock(_basicObjectClass.Name, _basicObjectClass);
  579. _objectClass.SetConstantNoMutateNoLock(_moduleClass.Name, _moduleClass);
  580. _objectClass.SetConstantNoMutateNoLock(_classClass.Name, _classClass);
  581. _objectClass.SetConstantNoMutateNoLock(_objectClass.Name, _objectClass);
  582. _objectClass.SetConstantNoMutateNoLock(_kernelModule.Name, _kernelModule);
  583. }
  584. AddModuleToCacheNoLock(typeof(BasicObject), _basicObjectClass);
  585. AddModuleToCacheNoLock(typeof(Kernel), _kernelModule);
  586. AddModuleToCacheNoLock(objectTracker.Type, _objectClass);
  587. AddModuleToCacheNoLock(typeof(RubyObject), _objectClass);
  588. AddModuleToCacheNoLock(_moduleClass.GetUnderlyingSystemType(), _moduleClass);
  589. AddModuleToCacheNoLock(_classClass.GetUnderlyingSystemType(), _classClass);
  590. }
  591. #endregion
  592. #region CLR Types and Namespaces
  593. private void AssemblyLoaded(Assembly/*!*/ assembly) {
  594. _namespaces.LoadAssembly(assembly);
  595. AddExtensionAssembly(assembly);
  596. }
  597. internal void AddModuleToCacheNoLock(Type/*!*/ type, RubyModule/*!*/ module) {
  598. Assert.NotNull(type, module);
  599. _moduleCache.Add(type, module);
  600. }
  601. internal void AddNamespaceToCacheNoLock(NamespaceTracker/*!*/ namespaceTracker, RubyModule/*!*/ module) {
  602. Assert.NotNull(namespaceTracker, module);
  603. _namespaceCache.Add(namespaceTracker, module);
  604. }
  605. internal RubyModule/*!*/ GetOrCreateModule(NamespaceTracker/*!*/ tracker) {
  606. Assert.NotNull(tracker);
  607. lock (ModuleCacheLock) {
  608. return GetOrCreateModuleNoLock(tracker);
  609. }
  610. }
  611. internal bool TryGetModule(NamespaceTracker/*!*/ namespaceTracker, out RubyModule result) {
  612. lock (NamespaceCacheLock) {
  613. return _namespaceCache.TryGetValue(namespaceTracker, out result);
  614. }
  615. }
  616. internal RubyModule/*!*/ GetOrCreateModule(Type/*!*/ moduleType) {
  617. Debug.Assert(RubyModule.IsModuleType(moduleType));
  618. lock (ModuleCacheLock) {
  619. return GetOrCreateModuleNoLock(moduleType);
  620. }
  621. }
  622. public bool TryGetModule(Type/*!*/ type, out RubyModule result) {
  623. lock (ModuleCacheLock) {
  624. return _moduleCache.TryGetValue(type, out result);
  625. }
  626. }
  627. internal bool TryGetModuleNoLock(Type/*!*/ type, out RubyModule result) {
  628. return _moduleCache.TryGetValue(type, out result);
  629. }
  630. internal bool TryGetClassNoLock(Type/*!*/ type, out RubyClass result) {
  631. RubyModule module;
  632. if (_moduleCache.TryGetValue(type, out module)) {
  633. result = module as RubyClass;
  634. if (result == null) {
  635. throw new InvalidOperationException("Specified type doesn't represent a class");
  636. }
  637. return true;
  638. } else {
  639. result = null;
  640. return false;
  641. }
  642. }
  643. internal RubyClass/*!*/ GetOrCreateClass(Type/*!*/ type) {
  644. lock (ModuleCacheLock) {
  645. return GetOrCreateClassNoLock(type);
  646. }
  647. }
  648. private RubyModule/*!*/ GetOrCreateModuleNoLock(NamespaceTracker/*!*/ tracker) {
  649. Assert.NotNull(tracker);
  650. RubyModule result;
  651. if (_namespaceCache.TryGetValue(tracker, out result)) {
  652. return result;
  653. }
  654. result = CreateModule(GetQualifiedName(tracker), null, null, null, null, tracker, null, ModuleRestrictions.None);
  655. _namespaceCache[tracker] = result;
  656. return result;
  657. }
  658. private RubyModule/*!*/ GetOrCreateModuleNoLock(Type/*!*/ moduleType) {
  659. Debug.Assert(RubyModule.IsModuleType(moduleType));
  660. RubyModule result;
  661. if (_moduleCache.TryGetValue(moduleType, out result)) {
  662. return result;
  663. }
  664. TypeTracker tracker = (TypeTracker)TypeTracker.FromMemberInfo(moduleType);
  665. RubyModule[] mixins;
  666. if (moduleType.IsGenericType && !moduleType.IsGenericTypeDefinition) {
  667. // I<T0..Tn> mixes in its generic definition I<,..,>
  668. mixins = new[] { GetOrCreateModuleNoLock(moduleType.GetGenericTypeDefinition()) };
  669. } else {
  670. mixins = null;
  671. }
  672. result = CreateModule(GetQualifiedNameNoLock(moduleType), null, null, null, mixins, null, tracker, ModuleRestrictions.None);
  673. _moduleCache[moduleType] = result;
  674. return result;
  675. }
  676. private RubyClass/*!*/ GetOrCreateClassNoLock(Type/*!*/ type) {
  677. Debug.Assert(!RubyModule.IsModuleType(type));
  678. RubyClass result;
  679. if (TryGetClassNoLock(type, out result)) {
  680. return result;
  681. }
  682. RubyClass baseClass;
  683. if (type.IsByRef) {
  684. baseClass = _objectClass;
  685. } else {
  686. baseClass = GetOrCreateClassNoLock(type.BaseType);
  687. }
  688. TypeTracker tracker = (TypeTracker)TypeTracker.FromMemberInfo(type);
  689. RubyModule[] clrMixins = GetClrMixinsNoLock(type);
  690. RubyModule[] expandedMixins;
  691. if (clrMixins != null) {
  692. using (ClassHierarchyLocker()) {
  693. expandedMixins = RubyModule.ExpandMixinsNoLock(baseClass, clrMixins);
  694. }
  695. } else {
  696. expandedMixins = RubyModule.EmptyArray;
  697. }
  698. result = CreateClass(
  699. GetQualifiedNameNoLock(type), type, null, null, null, null, null,
  700. baseClass, expandedMixins, tracker, null, false, false, ModuleRestrictions.None
  701. );
  702. if (TypeUtils.IsComObjectType(type)) {
  703. _comObjectClass = result;
  704. }
  705. _moduleCache[type] = result;
  706. return result;
  707. }
  708. /// <summary>
  709. /// An interface is mixed into the type that implements it.
  710. /// A generic type definition is mixed into its instantiations.
  711. ///
  712. /// In both cases these modules don't themselves contribute any callable CLR methods
  713. /// yet they might contribute CLR extension methods and Ruby methods defined on them.
  714. /// </summary>
  715. private RubyModule[] GetClrMixinsNoLock(Type/*!*/ type) {
  716. List<RubyModule> modules = new List<RubyModule>();
  717. if (type.IsGenericType && !type.IsGenericTypeDefinition) {
  718. modules.Add(GetOrCreateModuleNoLock(type.GetGenericTypeDefinition()));
  719. }
  720. if (type.IsArray) {
  721. if (type.GetArrayRank() > 1) {
  722. RubyModule module;
  723. if (TryGetModuleNoLock(typeof(MultiDimensionalArray), out module)) {
  724. modules.Add(module);
  725. }
  726. }
  727. } else if (type.IsEnum) {
  728. if (type.IsDefined(typeof(FlagsAttribute), false)) {
  729. RubyModule module;
  730. if (TryGetModuleNoLock(typeof(FlagEnumeration), out module)) {
  731. modules.Add(module);
  732. }
  733. }
  734. }
  735. foreach (Type iface in ReflectionUtils.GetDeclaredInterfaces(type)) {
  736. modules.Add(GetOrCreateModuleNoLock(iface));
  737. }
  738. return modules.Count > 0 ? modules.ToArray() : null;
  739. }
  740. #endregion
  741. #region CLR Extension Methods
  742. private readonly object ExtensionsLock = new object();
  743. // List of assemblies that might include extension methods but whose processing was delayed until the first call of use_clr_extensions.
  744. // Null once use_clr_extensions has been called.
  745. private List<Assembly> _potentialExtensionAssemblies = new List<Assembly>();
  746. // A list of extension methods that are available for activation. Grouped by a declaring namespace.
  747. // Value is null if the namepsace has been activated.
  748. private Dictionary<string, List<IEnumerable<ExtensionMethodInfo>>> _availableExtensions;
  749. private void AddExtensionAssembly(Assembly/*!*/ assembly) {
  750. if (_potentialExtensionAssemblies != null) {
  751. lock (ExtensionsLock) {
  752. if (_potentialExtensionAssemblies != null) {
  753. _potentialExtensionAssemblies.Add(assembly);
  754. return;
  755. }
  756. }
  757. }
  758. LoadExtensions(ReflectionUtils.GetVisibleExtensionMethodGroups(assembly, true));
  759. }
  760. private void LoadExtensions(IEnumerable<KeyValuePair<string, IEnumerable<ExtensionMethodInfo>>>/*!*/ extensionMethodGroups) {
  761. List<IEnumerable<ExtensionMethodInfo>> immediatelyActivated = null;
  762. lock (ExtensionsLock) {
  763. foreach (var extensionMethodGroup in extensionMethodGroups) {
  764. if (_availableExtensions == null) {
  765. _availableExtensions = new Dictionary<string, List<IEnumerable<ExtensionMethodInfo>>>();
  766. }
  767. string ns = extensionMethodGroup.Key;
  768. List<IEnumerable<ExtensionMethodInfo>> extensions;
  769. if (_availableExtensions.TryGetValue(ns, out extensions)) {
  770. if (extensions == null) {
  771. if (immediatelyActivated == null) {
  772. immediatelyActivated = new List<IEnumerable<ExtensionMethodInfo>>();
  773. }
  774. extensions = immediatelyActivated;
  775. }
  776. } else {
  777. _availableExtensions.Add(ns, extensions = new List<IEnumerable<ExtensionMethodInfo>>());
  778. }
  779. extensions.Add(extensionMethodGroup.Value);
  780. }
  781. }
  782. if (immediatelyActivated != null) {
  783. ActivateExtensions(immediatelyActivated);
  784. }
  785. }
  786. public void ActivateExtensions(string/*!*/ @namespace) {
  787. ContractUtils.RequiresNotNull(@namespace, "namespace");
  788. Assembly[] assemblies = null;
  789. if (_potentialExtensionAssemblies != null) {
  790. lock (ExtensionsLock) {
  791. if (_potentialExtensionAssemblies != null) {
  792. assemblies = _potentialExtensionAssemblies.ToArray();
  793. _potentialExtensionAssemblies = null;
  794. }
  795. }
  796. }
  797. if (assemblies != null) {
  798. var extensionGroups = new List<KeyValuePair<string, IEnumerable<ExtensionMethodInfo>>>();
  799. foreach (var assembly in assemblies) {
  800. extensionGroups.AddRange(ReflectionUtils.GetVisibleExtensionMethodGroups(assembly, true));
  801. }
  802. LoadExtensions(extensionGroups);
  803. }
  804. List<IEnumerable<ExtensionMethodInfo>> extensions;
  805. lock (ExtensionsLock) {
  806. _availableExtensions.TryGetValue(@namespace, out extensions);
  807. // activate namespace:
  808. _availableExtensions[@namespace] = null;
  809. }
  810. if (extensions != null) {
  811. ActivateExtensions(extensions);
  812. }
  813. }
  814. private void ActivateExtensions(List<IEnumerable<ExtensionMethodInfo>>/*!*/ extensionLists) {
  815. var groupedByType = new Dictionary<Type, List<ExtensionMethodInfo>>();
  816. foreach (var extensionList in extensionLists) {
  817. foreach (var extension in extensionList) {
  818. Type extendedType = extension.ExtendedType;
  819. Debug.Assert(!extendedType.IsGenericTypeDefinition && !extendedType.IsPointer && !extendedType.IsByRef);
  820. Type target;
  821. if (extendedType.ContainsGenericParameters) {
  822. if (extendedType.IsGenericParameter) {
  823. // TODO: we can do better if there are constraints defined on the parameter
  824. target = typeof(object);
  825. } else {
  826. target = extendedType.IsArray ? typeof(Array) : extendedType.GetGenericTypeDefinition();
  827. }
  828. } else {
  829. target = extendedType;
  830. }
  831. List<ExtensionMethodInfo> list;
  832. if (!groupedByType.TryGetValue(target, out list)) {
  833. groupedByType.Add(target, list = new List<ExtensionMethodInfo>());
  834. }
  835. list.Add(extension);
  836. }
  837. }
  838. using (ClassHierarchyLocker()) {
  839. lock (ModuleCacheLock) {
  840. foreach (var entry in groupedByType) {
  841. Type target = entry.Key;
  842. var methods = entry.Value;
  843. RubyModule targetModule = (target.IsGenericTypeDefinition || target.IsInterface) ? GetOrCreateModuleNoLock(target) : GetOrCreateClassNoLock(target);
  844. targetModule.AddExtensionMethodsNoLock(methods);
  845. }
  846. }
  847. }
  848. }
  849. #endregion
  850. #region Class and Module Factories (thread-safe)
  851. /// <summary>
  852. /// Class factory. Do not use RubyClass constructor except for special cases (Object, Class, Module, singleton classes).
  853. /// </summary>
  854. internal RubyClass/*!*/ CreateClass(string name, Type type, object classSingletonOf,
  855. Action<RubyModule> instanceTrait, Action<RubyModule> classTrait, Action<RubyModule> constantsInitializer, Delegate/*!*/[] factories,
  856. RubyClass/*!*/ superClass, RubyModule/*!*/[] expandedMixins, TypeTracker tracker, RubyStruct.Info structInfo,
  857. bool isRubyClass, bool isSingletonClass, ModuleRestrictions restrictions) {
  858. Assert.NotNull(superClass);
  859. RubyClass result = new RubyClass(this, name, type, classSingletonOf,
  860. instanceTrait, constantsInitializer, factories, superClass, expandedMixins, tracker, structInfo,
  861. isRubyClass, isSingletonClass, restrictions
  862. );
  863. result.InitializeImmediateClass(superClass.ImmediateClass, classTrait);
  864. return result;
  865. }
  866. /// <summary>
  867. /// Module factory. Do not use RubyModule constructor except special cases (Kernel).
  868. /// </summary>
  869. internal RubyModule/*!*/ CreateModule(string name,
  870. Action<RubyModule> instanceTrait, Action<RubyModule> classTrait, Action<RubyModule> constantsInitializer,
  871. RubyModule/*!*/[] expandedMixins, NamespaceTracker namespaceTracker, TypeTracker typeTracker, ModuleRestrictions restrictions) {
  872. RubyModule result = new RubyModule(
  873. this, name, instanceTrait, constantsInitializer, expandedMixins, namespaceTracker, typeTracker, restrictions
  874. );
  875. result.InitializeImmediateClass(_moduleClass, classTrait);
  876. return result;
  877. }
  878. /// <summary>
  879. /// Creates a singleton class for specified object unless it already exists.
  880. /// </summary>
  881. public RubyClass/*!*/ GetOrCreateSingletonClass(object obj) {
  882. RubyModule module = obj as RubyModule;
  883. if (module != null) {
  884. return module.GetOrCreateSingletonClass();
  885. }
  886. return GetOrCreateInstanceSingleton(obj, null, null, null, null);
  887. }
  888. internal RubyClass/*!*/ GetOrCreateMainSingleton(object obj, RubyModule/*!*/[] expandedMixins) {
  889. return GetOrCreateInstanceSingleton(obj, _mainSingletonTrait, null, null, expandedMixins);
  890. }
  891. internal RubyClass/*!*/ GetOrCreateInstanceSingleton(object obj, Action<RubyModule> instanceTrait, Action<RubyModule> classTrait,
  892. Action<RubyModule> constantsInitializer, RubyModule/*!*/[] expandedMixins) {
  893. Debug.Assert(!(obj is RubyModule));
  894. Debug.Assert(RubyUtils.HasSingletonClass(obj));
  895. if (obj == null) {
  896. return _nilClass;
  897. }
  898. if (obj is bool) {
  899. return (bool)obj ? _trueClass : _falseClass;
  900. }
  901. RubyInstanceData data = null;
  902. RubyClass immediate = GetImmediateClassOf(obj, ref data);
  903. if (immediate.IsSingletonClass) {
  904. Debug.Assert(!immediate.IsDummySingletonClass);
  905. return immediate;
  906. }
  907. RubyClass result = CreateClass(
  908. null, null, obj, instanceTrait, classTrait, constantsInitializer, null,
  909. immediate, expandedMixins, null, null, true, true, ModuleRestrictions.None
  910. );
  911. using (ClassHierarchyLocker()) {
  912. // singleton might have been created by another thread:
  913. immediate = GetImmediateClassOf(obj, ref data);
  914. if (immediate.IsSingletonClass) {
  915. Debug.Assert(!immediate.IsDummySingletonClass);
  916. return immediate;
  917. }
  918. SetInstanceSingletonOfNoLock(obj, ref data, result);
  919. if (!(obj is IRubyObject)) {
  920. PerfTrack.NoteEvent(PerfTrack.Categories.Count, "Non-IRO singleton created " + immediate.NominalClass.Name);
  921. }
  922. }
  923. Debug.Assert(result.IsSingletonClass && !result.IsDummySingletonClass);
  924. return result;
  925. }
  926. /// <summary>
  927. /// Defines a new module nested in the given owner.
  928. /// The module is published into the global scope if the owner is Object.
  929. ///
  930. /// Thread safe.
  931. /// </summary>
  932. internal RubyModule/*!*/ DefineModule(RubyModule/*!*/ owner, string/*!*/ name) {
  933. RubyModule result = CreateModule(owner.MakeNestedModuleName(name), null, null, null, null, null, null, ModuleRestrictions.None);
  934. PublishModule(name, owner, result);
  935. return result;
  936. }
  937. /// <summary>
  938. /// Defines a new class nested in the given owner.
  939. /// The module is published into the global scope it if is not anonymous and the owner is Object.
  940. ///
  941. /// Thread safe.
  942. /// Triggers "inherited" event.
  943. /// </summary>
  944. internal RubyClass/*!*/ DefineClass(RubyModule/*!*/ owner, string name, RubyClass/*!*/ superClass, RubyStruct.Info structInfo) {
  945. Assert.NotNull(owner, superClass);
  946. if (superClass.TypeTracker != null && superClass.TypeTracker.Type.ContainsGenericParameters) {
  947. throw RubyExceptions.CreateTypeError(String.Format(
  948. "{0}: cannot inherit from open generic instantiation {1}. Only closed instantiations are supported.",
  949. name, superClass.Name
  950. ));
  951. }
  952. string qualifiedName = owner.MakeNestedModuleName(name);
  953. RubyClass result = CreateClass(
  954. qualifiedName, null, null, null, null, null, null, superClass, null, null, structInfo, true, false, ModuleRestrictions.None
  955. );
  956. PublishModule(name, owner, result);
  957. superClass.ClassInheritedEvent(result);
  958. return result;
  959. }
  960. private static void PublishModule(string name, RubyModule/*!*/ owner, RubyModule/*!*/ module) {
  961. if (name != null) {
  962. owner.SetConstant(name, module);
  963. if (owner.IsObjectClass) {
  964. module.Publish(name);
  965. }
  966. }
  967. }
  968. #endregion
  969. #region Libraries (thread-safe)
  970. //
  971. // Scenarios:
  972. // 1) define/reopen Ruby class/module (name != null && !builtin)
  973. // - Built-in definitions don't reopen existing Ruby classes/modules as they are all declared before any Ruby code can run.
  974. // - Only global classes/modules can be reopened (TODO: we need to pass in the containing class).
  975. // - If reopening:
  976. // - Members are merged into the existing Ruby class/module.
  977. // - Underlying system type is ignored when extending an existing Ruby class by a library definition.
  978. // We don't want to fail the library load based upon an existence of an instance of a Ruby class (whose CLR type we cannot change).
  979. //
  980. // 2) extend CLR type (name == null)
  981. //
  982. private T PrepareLibraryModuleDefinition<T>(string name, RubyClass super, RubyModule/*!*/[]/*!*/ mixins, ModuleRestrictions restrictions, bool builtin,
  983. out RubyModule[] expandedMixins) where T : RubyModule {
  984. using (ClassHierarchyLocker()) {
  985. expandedMixins = RubyModule.ExpandMixinsNoLock(super, mixins);
  986. if (name != null && !builtin) {
  987. // Do not run constant initializer - all modules that the name might refer to should have already been set up;
  988. // A library definition should only reopen Ruby class/module definition, not another library definition.
  989. ConstantStorage c;
  990. if (_objectClass.TryGetConstantNoAutoloadNoInit(name, out c)) {
  991. var result = c.Value as T;
  992. bool isClass = typeof(T) == typeof(RubyClass);
  993. if (result == null || result.IsClass != isClass) {
  994. throw RubyExceptions.CreateTypeError("`{0}' is not a {1}", name, isClass ? "class" : "module");
  995. }
  996. if (isClass && (restrictions & ModuleRestrictions.AllowReopening) == 0) {
  997. throw RubyExceptions.CreateTypeError("cannot redefine {1} `{0}'", name, isClass ? "class" : "module");
  998. }
  999. return result;
  1000. }
  1001. }
  1002. }
  1003. return null;
  1004. }
  1005. internal RubyModule/*!*/ DefineLibraryModule(string name, Type/*!*/ type,
  1006. Action<RubyModule> instanceTrait, Action<RubyModule> classTrait, Action<RubyModule> constantsInitializer,
  1007. RubyModule/*!*/[]/*!*/ mixins, ModuleRestrictions restrictions, bool builtin) {
  1008. Assert.NotNull(type);
  1009. Assert.NotNullItems(mixins);
  1010. Debug.Assert(name == null || name.Length != 0);
  1011. Debug.Assert(name != null || (restrictions & ModuleRestrictions.NoUnderlyingType) == 0);
  1012. RubyModule[] expandedMixins;
  1013. RubyModule result = PrepareLibraryModuleDefinition<RubyModule>(name, null, mixins, restrictions, builtin, out expandedMixins);
  1014. bool exists = result != null;
  1015. if (!exists) {
  1016. lock (ModuleCacheLock) {
  1017. if (!(exists = TryGetModuleNoLock(type, out result))) {
  1018. if (name == null) {
  1019. name = GetQualifiedNameNoLock(type);
  1020. }
  1021. // Use empty constant initializer rather than null so that we don't try to initialize nested types.
  1022. result = CreateModule(
  1023. name, instanceTrait, classTrait, constantsInitializer ?? RubyModule.EmptyInitializer, expandedMixins, null,
  1024. GetLibraryModuleTypeTracker(type, restrictions),
  1025. restrictions
  1026. );
  1027. AddModuleToCacheNoLock(type, result);
  1028. }
  1029. }
  1030. }
  1031. if (exists) {
  1032. result.IncludeLibraryModule(instanceTrait, classTrait, constantsInitializer, mixins, builtin);
  1033. }
  1034. return result;
  1035. }
  1036. internal RubyClass/*!*/ DefineLibraryClass(string name, Type/*!*/ type,
  1037. Action<RubyModule> instanceTrait, Action<RubyModule> classTrait, Action<RubyModule> constantsInitializer,
  1038. RubyClass super, RubyModule[]/*!*/ mixins, Delegate/*!*/[] factories, ModuleRestrictions restrictions, bool builtin) {
  1039. Assert.NotNull(type);
  1040. Assert.NotNullItems(mixins);
  1041. Debug.Assert(name != null || (restrictions & ModuleRestrictions.NoUnderlyingType) == 0);
  1042. Debug.Assert(name == null || name.Length != 0);
  1043. RubyModule[] expandedMixins;
  1044. RubyClass result = PrepareLibraryModuleDefinition<RubyClass>(name, super, mixins, restrictions, builtin, out expandedMixins);
  1045. bool exists = result != null;
  1046. if (!exists) {
  1047. lock (ModuleCacheLock) {
  1048. if (!(exists = TryGetClassNoLock(type, out result))) {
  1049. if (name == null) {
  1050. name = GetQualifiedNameNoLock(type);
  1051. }
  1052. if (super == null) {
  1053. super = GetOrCreateClassNoLock(type.BaseType);
  1054. }
  1055. // Use empty constant initializer rather than null so that we don't try to initialize nested types.
  1056. result = CreateClass(
  1057. name, type, null, instanceTrait, classTrait, constantsInitializer ?? RubyModule.EmptyInitializer, factories,
  1058. super, expandedMixins, GetLibraryModuleTypeTracker(type, restrictions), null, false, false,
  1059. restrictions
  1060. );
  1061. AddModuleToCacheNoLock(type, result);
  1062. }
  1063. }
  1064. }
  1065. if (exists) {
  1066. if (super != null && super != result.SuperClass) {
  1067. throw RubyExceptions.CreateTypeError("superclass mismatch for class {0}", name);
  1068. }
  1069. if (factories != null && factories.Length != 0) {
  1070. throw RubyExceptions.CreateTypeError("Cannot add factories to an existing class");
  1071. }
  1072. result.IncludeLibraryModule(instanceTrait, classTrait, constantsInitializer, mixins, builtin);
  1073. return result;
  1074. } else if (!builtin) {
  1075. super.ClassInheritedEvent(result);
  1076. }
  1077. return result;
  1078. }
  1079. private static TypeTracker GetLibraryModuleTypeTracker(Type/*!*/ type, ModuleRestrictions restrictions) {
  1080. return (restrictions & ModuleRestrictions.NoUnderlyingType) != 0 ? null : ReflectionCache.GetTypeTracker(type);
  1081. }
  1082. #endregion
  1083. #region Getting Modules and Classes from objects, CLR types and CLR namespaces (thread-safe)
  1084. public RubyModule/*!*/ GetModule(Type/*!*/ type) {
  1085. if (RubyModule.IsModuleType(type)) {
  1086. return GetOrCreateModule(type);
  1087. } else {
  1088. return GetOrCreateClass(type);
  1089. }
  1090. }
  1091. public RubyModule/*!*/ GetModule(NamespaceTracker/*!*/ namespaceTracker) {
  1092. return GetOrCreateModule(namespaceTracker);
  1093. }
  1094. public RubyClass/*!*/ GetClass(Type/*!*/ type) {
  1095. ContractUtils.Requires(!RubyModule.IsModuleType(type));
  1096. return GetOrCreateClass(type);
  1097. }
  1098. /// <summary>
  1099. /// Gets a class of the specified object (skips any singletons).
  1100. /// </summary>
  1101. public RubyClass/*!*/ GetClassOf(object obj) {
  1102. ContractUtils.Ensures(!ContractUtils.Result<RubyClass>().IsSingletonClass);
  1103. return TryGetClassOfRubyObject(obj) ?? GetOrCreateClass(obj.GetType());
  1104. }
  1105. private RubyClass TryGetClassOfRubyObject(object obj) {
  1106. if (obj == null) {
  1107. return _nilClass;
  1108. }
  1109. if (obj is bool) {
  1110. return (bool)obj ? _trueClass : _falseClass;
  1111. }
  1112. IRubyObject rubyObj = obj as IRubyObject;
  1113. if (rubyObj != null) {
  1114. var result = rubyObj.ImmediateClass.GetNonSingletonClass();
  1115. Debug.Assert(result != null, "Invalid IRubyObject implementation: Class should not be null");
  1116. return result;
  1117. }
  1118. return null;
  1119. }
  1120. /// <summary>
  1121. /// Gets a singleton or class for <c>obj</c>.
  1122. /// Might return a class object from a foreign runtime (if obj is a runtime bound object).
  1123. /// </summary>
  1124. public RubyClass/*!*/ GetImmediateClassOf(object obj) {
  1125. RubyInstanceData data = null;
  1126. return GetImmediateClassOf(obj, ref data);
  1127. }
  1128. private RubyClass/*!*/ GetImmediateClassOf(object obj, ref RubyInstanceData data) {
  1129. RubyClass result = TryGetImmediateClassOf(obj, ref data);
  1130. if (result != null) {
  1131. return result;
  1132. }
  1133. result = GetClassOf(obj);
  1134. if (data != null) {
  1135. data.UpdateImmediateClass(result);
  1136. }
  1137. return result;
  1138. }
  1139. // thread-safety:
  1140. // If the immediate class reference is being changed (a singleton is being defined) during this operation
  1141. // it is undefined which one of the classes we return.
  1142. private RubyClass TryGetImmediateClassOf(object obj, ref RubyInstanceData data) {
  1143. IRubyObject rubyObj = obj as IRubyObject;
  1144. if (rubyObj != null) {
  1145. return rubyObj.ImmediateClass;
  1146. } else if (data != null || (data = TryGetInstanceData(obj)) != null) {
  1147. return data.ImmediateClass;
  1148. } else {
  1149. return null;
  1150. }
  1151. }
  1152. // thread-safety: must only be run under a lock that prevents singleton creation on the target object:
  1153. private void SetInstanceSingletonOfNoLock(object obj, ref RubyInstanceData data, RubyClass/*!*/ singleton) {
  1154. RequiresClassHierarchyLock();
  1155. Debug.Assert(!(obj is RubyModule) && singleton != null);
  1156. IRubyObject rubyObj = obj as IRubyObject;
  1157. if (rubyObj != null) {
  1158. rubyObj.ImmediateClass = singleton;
  1159. } else if (data != null) {
  1160. data.ImmediateClass = singleton;
  1161. } else {
  1162. (data = GetInstanceData(obj)).ImmediateClass = singleton;
  1163. }
  1164. }
  1165. internal RubyClass TryGetSingletonOf(object obj, ref RubyInstanceData data) {
  1166. RubyClass immediate = TryGetImmediateClassOf(obj, ref data);
  1167. return immediate != null ? (immediate.IsSingletonClass ? immediate : null) : null;
  1168. }
  1169. public bool IsKindOf(object obj, RubyModule/*!*/ m) {
  1170. return GetImmediateClassOf(obj).HasAncestor(m);
  1171. }
  1172. public bool IsInstanceOf(object value, object classObject) {
  1173. RubyClass c = classObject as RubyClass;
  1174. if (c != null) {
  1175. return GetClassOf(value).IsSubclassOf(c);
  1176. }
  1177. return false;
  1178. }
  1179. #endregion
  1180. #region Module Names
  1181. /// <summary>
  1182. /// Gets the Ruby name of the class of the given object.
  1183. /// </summary>
  1184. public string/*!*/ GetClassName(object obj) {
  1185. return GetClassName(obj, false);
  1186. }
  1187. /// <summary>
  1188. /// Gets the display name of the class of the given object.
  1189. /// Includes singleton names.
  1190. /// </summary>
  1191. public string/*!*/ GetClassDisplayName(object obj) {
  1192. return GetClassName(obj, true);
  1193. }
  1194. private string/*!*/ GetClassName(object obj, bool display) {
  1195. // doesn't create a RubyClass for .NET types
  1196. RubyClass cls = TryGetClassOfRubyObject(obj);
  1197. if (cls != null) {
  1198. return cls.Name;
  1199. }
  1200. return GetTypeName(obj.GetType(), display);
  1201. }
  1202. public string/*!*/ GetTypeName(Type/*!*/ type, bool display) {
  1203. RubyModule module;
  1204. lock (ModuleCacheLock) {
  1205. if (TryGetModuleNoLock(type, out module)) {
  1206. if (display) {
  1207. return module.GetDisplayName(this, false).ToString();
  1208. } else {
  1209. return module.Name;
  1210. }
  1211. } else {
  1212. return GetQualifiedNameNoLock(type);
  1213. }
  1214. }
  1215. }
  1216. private string/*!*/ GetQualifiedNameNoLock(Type/*!*/ type) {
  1217. return GetQualifiedNameNoLock(type, this, false);
  1218. }
  1219. internal static string/*!*/ GetQualifiedNameNoLock(Type/*!*/ type, RubyContext context, bool noGenericArgs) {
  1220. return AppendQualifiedNameNoLock(new StringBuilder(), type, context, noGenericArgs).ToString();
  1221. }
  1222. private static StringBuilder/*!*/ AppendQualifiedNameNoLock(StringBuilder/*!*/ result, Type/*!*/ type, RubyContext context, bool noGenericArgs) {
  1223. if (type.IsGenericParameter) {
  1224. return result.Append(type.Name);
  1225. }
  1226. // arrays, by-refs, pointers:
  1227. Type elementType = type.GetElementType();
  1228. if (elementType != null) {
  1229. AppendQualifiedNameNoLock(result, elementType, context, noGenericArgs);
  1230. if (type.IsByRef) {
  1231. result.Append('&');
  1232. } else if (type.IsArray) {
  1233. result.Append('[');
  1234. result.Append(',', type.GetArrayRank() - 1);
  1235. result.Append(']');
  1236. } else {
  1237. Debug.Assert(type.IsPointer);
  1238. result.Append('*');
  1239. }
  1240. return result;
  1241. }
  1242. // qualifiers:
  1243. if (type.DeclaringType != null) {
  1244. AppendQualifiedNameNoLock(result, type.DeclaringType, context, noGenericArgs);
  1245. result.Append("::");
  1246. } else if (type.Namespace != null) {
  1247. result.Append(type.Namespace.Replace(Type.Delimiter.ToString(), "::"));
  1248. result.Append("::");
  1249. }
  1250. result.Append(ReflectionUtils.GetNormalizedTypeName(type));
  1251. // generic args:
  1252. if (!noGenericArgs && type.IsGenericType) {
  1253. result.Append("[");
  1254. var genericArgs = type.GetGenericArguments();
  1255. for (int i = 0; i < genericArgs.Length; i++) {
  1256. if (i > 0) {
  1257. result.Append(", ");
  1258. }
  1259. RubyModule module;
  1260. if (context != null && context.TryGetModuleNoLock(genericArgs[i], out module)) {
  1261. result.Append(module.Name);
  1262. } else {
  1263. AppendQualifiedNameNoLock(result, genericArgs[i], context, noGenericArgs);
  1264. }
  1265. }
  1266. result.Append("]");
  1267. }
  1268. return result;
  1269. }
  1270. private static string/*!*/ GetQualifiedName(NamespaceTracker/*!*/ namespaceTracker) {
  1271. ContractUtils.RequiresNotNull(namespaceTracker, "namespaceTracker");
  1272. if (namespaceTracker.Name == null) return String.Empty;
  1273. return namespaceTracker.Name.Replace(Type.Delimiter.ToString(), "::");
  1274. }
  1275. #endregion
  1276. #region Member Resolution (thread-safe)
  1277. // thread-safe:
  1278. public MethodResolutionResult ResolveMethod(object target, string/*!*/ name, bool includePrivate) {
  1279. var owner = GetImmediateClassOf(target);
  1280. return owner.ResolveMethod(name, includePrivate ? VisibilityContext.AllVisible : new VisibilityContext(owner));
  1281. }
  1282. // thread-safe:
  1283. public MethodResolutionResult ResolveMethod(object target, string/*!*/ name, VisibilityContext visibility) {
  1284. return GetImmediateClassOf(target).ResolveMethod(name, visibility);
  1285. }
  1286. // thread-safe:
  1287. public bool TryGetModule(RubyGlobalScope autoloadScope, string/*!*/ moduleName, out RubyModule result) {
  1288. using (ClassHierarchyLocker()) {
  1289. result = _objectClass;
  1290. int pos = 0;
  1291. while (true) {
  1292. int pos2 = moduleName.IndexOf("::", pos, StringComparison.Ordinal);
  1293. string partialName;
  1294. if (pos2 < 0) {
  1295. partialName = moduleName.Substring(pos);
  1296. } else {
  1297. partialName = moduleName.Substring(pos, pos2 - pos);
  1298. pos = pos2 + 2;
  1299. }
  1300. ConstantStorage tmp;
  1301. if (!result.TryResolveConstantNoLock(autoloadScope, partialName, out tmp)) {
  1302. result = null;
  1303. return false;
  1304. }
  1305. result = tmp.Value as RubyModule;
  1306. if (result == null) {
  1307. return false;
  1308. } else if (pos2 < 0) {
  1309. return true;
  1310. }
  1311. }
  1312. }
  1313. }
  1314. // thread-safe:
  1315. public object ResolveMissingConstant(RubyModule/*!*/ owner, string/*!*/ name) {
  1316. if (owner.IsObjectClass) {
  1317. object value;
  1318. if (RubyOps.TryGetGlobalScopeConstant(this, _globalScope, name, out value)) {
  1319. return value;
  1320. }
  1321. if ((value = _namespaces.TryGetPackageAny(name)) != null) {
  1322. return TrackerToModule(value);
  1323. }
  1324. }
  1325. throw RubyExceptions.CreateNameError(String.Format("uninitialized constant {0}::{1}", owner.Name, name));
  1326. }
  1327. // thread-safe:
  1328. internal object TrackerToModule(object value) {
  1329. TypeGroup typeGroup = value as TypeGroup;
  1330. if (typeGroup != null) {
  1331. return value;
  1332. }
  1333. // TypeTracker retrieved from namespace tracker should behave like a RubyClass/RubyModule:
  1334. TypeTracker typeTracker = value as TypeTracker;
  1335. if (typeTracker != null) {
  1336. return GetModule(typeTracker.Type);
  1337. }
  1338. // NamespaceTracker retrieved from namespace tracker should behave like a RubyModule:
  1339. NamespaceTracker namespaceTracker = value as NamespaceTracker;
  1340. if (namespaceTracker != null) {
  1341. return GetModule(namespaceTracker);
  1342. }
  1343. return value;
  1344. }
  1345. #endregion
  1346. #region Object Operations: InstanceData access (thread-safe)
  1347. // Retrieving instance data is thread safe. Operations on the instance data object are not.
  1348. internal RubyInstanceData TryGetInstanceData(object obj) {
  1349. IRubyObject rubyObject = obj as IRubyObject;
  1350. if (rubyObject != null) {
  1351. return rubyObject.TryGetInstanceData();
  1352. }
  1353. if (obj == null) {
  1354. return _nilInstanceData;
  1355. }
  1356. RubyInstanceData result;
  1357. if (!RubyUtils.HasObjectState(obj)) {
  1358. lock (ValueTypeInstanceDataLock) {
  1359. _valueTypeInstanceData.TryGetValue(obj, out result);
  1360. }
  1361. return result;
  1362. }
  1363. TryGetClrTypeInstanceData(obj, out result);
  1364. return result;
  1365. }
  1366. internal bool TryGetClrTypeInstanceData(object/*!*/ obj, out RubyInstanceData result) {
  1367. lock (ReferenceTypeInstanceDataLock) {
  1368. return _referenceTypeInstanceData.TryGetValue(obj, out result);
  1369. }
  1370. }
  1371. internal RubyInstanceData/*!*/ GetInstanceData(object obj) {
  1372. IRubyObject rubyObject = obj as IRubyObject;
  1373. if (rubyObject != null) {
  1374. return rubyObject.GetInstanceData();
  1375. }
  1376. if (obj == null) {
  1377. return _nilInstanceData;
  1378. }
  1379. RubyInstanceData result;
  1380. if (!RubyUtils.HasObjectState(obj)) {
  1381. lock (ValueTypeInstanceDataLock) {
  1382. if (!_valueTypeInstanceData.TryGetValue(obj, out result)) {
  1383. _valueTypeInstanceData.Add(obj, result = new RubyInstanceData());
  1384. }
  1385. }
  1386. return result;
  1387. }
  1388. lock (ReferenceTypeInstanceDataLock) {
  1389. if (!_referenceTypeInstanceData.TryGetValue(obj, out result)) {
  1390. _referenceTypeInstanceData.Add(obj, result = new RubyInstanceData());
  1391. }
  1392. }
  1393. return result;
  1394. }
  1395. #endregion
  1396. #region Object Operations: Instance variables, flags (NOT thread-safe)
  1397. public bool HasInstanceVariables(object obj) {
  1398. RubyInstanceData data = TryGetInstanceData(obj);
  1399. return data != null && data.HasInstanceVariables;
  1400. }
  1401. public string[]/*!*/ GetInstanceVariableNames(object obj) {
  1402. RubyInstanceData data = TryGetInstanceData(obj);
  1403. return (data != null) ? data.GetInstanceVariableNames() : ArrayUtils.EmptyStrings;
  1404. }
  1405. public bool TryGetInstanceVariable(object obj, string/*!*/ name, out object value) {
  1406. RubyInstanceData data = TryGetInstanceData(obj);
  1407. if (data == null || !data.TryGetInstanceVariable(name, out value)) {
  1408. value = null;
  1409. return false;
  1410. }
  1411. return true;
  1412. }
  1413. private RubyInstanceData MutateInstanceVariables(object obj) {
  1414. RubyInstanceData data;
  1415. if (IsObjectFrozen(obj, out data)) {
  1416. throw RubyExceptions.CreateObjectFrozenError();
  1417. }
  1418. return data;
  1419. }
  1420. public void SetInstanceVariable(object obj, string/*!*/ name, object value) {
  1421. (MutateInstanceVariables(obj) ?? GetInstanceData(obj)).SetInstanceVariable(name, value);
  1422. }
  1423. public bool TryRemoveInstanceVariable(object obj, string/*!*/ name, out object value) {
  1424. RubyInstanceData data = MutateInstanceVariables(obj) ?? TryGetInstanceData(obj);
  1425. if (data == null || !data.TryRemoveInstanceVariable(name, out value)) {
  1426. value = null;
  1427. return false;
  1428. }
  1429. return true;
  1430. }
  1431. //
  1432. // Thread safety: target object must be a fresh object not be shared with other threads:
  1433. //
  1434. // Copies instance variables from source to target object.
  1435. // If the source has a singleton class it's members are copied to the target as well.
  1436. // Assumes a fresh instance of target, with no instance data.
  1437. //
  1438. internal void CopyInstanceData(object source, object target, bool copySingletonMembers) {
  1439. RubyInstanceData targetData = null;
  1440. Debug.Assert(!copySingletonMembers || !(source is RubyModule));
  1441. Debug.Assert(TryGetInstanceData(target) == null);
  1442. // target object is not a singleton:
  1443. Debug.Assert(!copySingletonMembers || TryGetSingletonOf(target, ref targetData) == null && targetData == null);
  1444. RubyInstanceData sourceData = TryGetInstanceData(source);
  1445. if (sourceData != null) {
  1446. if (sourceData.HasInstanceVariables) {
  1447. sourceData.CopyInstanceVariablesTo(targetData = GetInstanceData(target));
  1448. }
  1449. }
  1450. if (copySingletonMembers) {
  1451. using (ClassHierarchyLocker()) {
  1452. RubyClass singleton = TryGetSingletonOf(source, ref sourceData);
  1453. if (singleton != null) {
  1454. var singletonDup = singleton.Duplicate(target);
  1455. singletonDup.InitializeMembersFrom(singleton);
  1456. SetInstanceSingletonOfNoLock(target, ref targetData, singletonDup);
  1457. }
  1458. }
  1459. }
  1460. }
  1461. public IRubyObjectState/*!*/ GetObjectState(object/*!*/ obj) {
  1462. return obj as IRubyObjectState ?? GetInstanceData(obj);
  1463. }
  1464. public IRubyObjectState TryGetObjectState(object/*!*/ obj) {
  1465. return obj as IRubyObjectState ?? TryGetInstanceData(obj);
  1466. }
  1467. public bool IsObjectFrozen(object obj) {
  1468. RubyInstanceData data;
  1469. return IsObjectFrozen(obj, out data);
  1470. }
  1471. private bool IsObjectFrozen(object obj, out RubyInstanceData data) {
  1472. var state = obj as IRubyObjectState;
  1473. if (state != null) {
  1474. data = null;
  1475. return state.IsFrozen;
  1476. }
  1477. data = TryGetInstanceData(obj);
  1478. return data != null ? data.IsFrozen : false;
  1479. }
  1480. public bool IsObjectTainted(object obj) {
  1481. var state = TryGetObjectState(obj);
  1482. return state != null ? state.IsTainted : false;
  1483. }
  1484. public bool IsObjectUntrusted(object obj) {
  1485. var state = TryGetObjectState(obj);
  1486. return state != null ? state.IsUntrusted : false;
  1487. }
  1488. public void GetObjectTrust(object obj, out bool tainted, out bool untrusted) {
  1489. var state = TryGetObjectState(obj);
  1490. if (state != null) {
  1491. tainted = state.IsTainted;
  1492. untrusted = state.IsUntrusted;
  1493. } else {
  1494. tainted = false;
  1495. untrusted = false; // TODO: default?
  1496. }
  1497. }
  1498. public void FreezeObject(object obj) {
  1499. GetObjectState(obj).Freeze();
  1500. }
  1501. public void SetObjectTaint(object obj, bool taint) {
  1502. GetObjectState(obj).IsTainted = taint;
  1503. }
  1504. public void SetObjectTrustiness(object obj, bool untrusted) {
  1505. GetObjectState(obj).IsUntrusted = untrusted;
  1506. }
  1507. public object TaintObjectBy(object obj, object source) {
  1508. var sourceState = TryGetObjectState(source);
  1509. if (sourceState != null) {
  1510. bool tainted = sourceState.IsTainted;
  1511. bool untrusted = sourceState.IsUntrusted;
  1512. if (tainted || untrusted) {
  1513. var state = GetObjectState(obj);
  1514. state.IsTainted |= tainted;
  1515. state.IsUntrusted |= untrusted;
  1516. }
  1517. }
  1518. return obj;
  1519. }
  1520. public object FreezeObjectBy(object obj, object source) {
  1521. var sourceState = TryGetObjectState(source);
  1522. if (sourceState != null && sourceState.IsFrozen) {
  1523. GetObjectState(obj).Freeze();
  1524. }
  1525. return obj;
  1526. }
  1527. #endregion
  1528. #region Dynamic Object Operations (thread-safe)
  1529. /// <summary>
  1530. /// Calls "inspect" and converts its result to a string using "to_s" protocol (<see cref="ConvertToSAction"/>).
  1531. /// </summary>
  1532. public MutableString/*!*/ Inspect(object obj) {
  1533. RubyClass cls = GetClassOf(obj);
  1534. var inspect = cls.InspectSite;
  1535. var toS = cls.InspectResultConversionSite;
  1536. return toS.Target(toS, inspect.Target(inspect, obj));
  1537. }
  1538. #endregion
  1539. #region Global Variables: General access (thread-safe)
  1540. public object GetGlobalVariable(string/*!*/ name) {
  1541. object value;
  1542. TryGetGlobalVariable(null, name, out value);
  1543. return value;
  1544. }
  1545. public void DefineGlobalVariable(string/*!*/ name, object value) {
  1546. lock (GlobalVariablesLock) {
  1547. _globalVariables[name] = new GlobalVariableInfo(value);
  1548. }
  1549. }
  1550. public void DefineReadOnlyGlobalVariable(string/*!*/ name, object value) {
  1551. lock (GlobalVariablesLock) {
  1552. _globalVariables[name] = new ReadOnlyGlobalVariableInfo(value);
  1553. }
  1554. }
  1555. public void DefineGlobalVariable(string/*!*/ name, GlobalVariable/*!*/ variable) {
  1556. ContractUtils.RequiresNotNull(variable, "variable");
  1557. lock (GlobalVariablesLock) {
  1558. _globalVariables[name] = variable;
  1559. }
  1560. }
  1561. internal void DefineGlobalVariableNoLock(string/*!*/ name, GlobalVariable/*!*/ variable) {
  1562. _globalVariables[name] = variable;
  1563. }
  1564. public bool DeleteGlobalVariable(string/*!*/ name) {
  1565. lock (GlobalVariablesLock) {
  1566. return _globalVariables.Remove(name);
  1567. }
  1568. }
  1569. public void AliasGlobalVariable(string/*!*/ newName, string/*!*/ oldName) {
  1570. lock (GlobalVariablesLock) {
  1571. GlobalVariable existing;
  1572. if (!_globalVariables.TryGetValue(oldName, out existing)) {
  1573. DefineGlobalVariableNoLock(oldName, existing = new GlobalVariableInfo(null, false));
  1574. }
  1575. _globalVariables[newName] = existing;
  1576. }
  1577. }
  1578. // null scope should be used only when accessed outside Ruby code; in that case no scoped variables are available
  1579. // we need scope here, bacause the variable might be an alias for scoped variable (regex matches):
  1580. public void SetGlobalVariable(RubyScope scope, string/*!*/ name, object value) {
  1581. lock (GlobalVariablesLock) {
  1582. GlobalVariable global;
  1583. if (_globalVariables.TryGetValue(name, out global)) {
  1584. global.SetValue(this, scope, name, value);
  1585. return;
  1586. }
  1587. _globalVariables[name] = new GlobalVariableInfo(value);
  1588. }
  1589. }
  1590. // null scope should be used only when accessed outside Ruby code; in that case no scoped variables are available
  1591. // we need scope here, bacause the variable might be an alias for scoped variable (regex matches):
  1592. public bool TryGetGlobalVariable(RubyScope scope, string/*!*/ name, out object value) {
  1593. lock (GlobalVariablesLock) {
  1594. GlobalVariable global;
  1595. if (_globalVariables.TryGetValue(name, out global)) {
  1596. value = global.GetValue(this, scope);
  1597. return true;
  1598. }
  1599. }
  1600. value = null;
  1601. return false;
  1602. }
  1603. internal bool TryGetGlobalVariable(string/*!*/ name, out GlobalVariable variable) {
  1604. lock (GlobalVariablesLock) {
  1605. return _globalVariables.TryGetValue(name, out variable);
  1606. }
  1607. }
  1608. #endregion
  1609. #region Global Variables: Special variables (thread-safe)
  1610. /// <summary>
  1611. /// $!
  1612. /// </summary>
  1613. [ThreadStatic]
  1614. private static Exception _currentException;
  1615. public Exception CurrentException {
  1616. get { return _currentException; }
  1617. internal set { _currentException = RubyUtils.GetVisibleException(value); }
  1618. }
  1619. internal Exception SetCurrentException(object value) {
  1620. Exception e = value as Exception;
  1621. // "$! = nil" is allowed
  1622. if (value != null && e == null) {
  1623. throw RubyExceptions.CreateTypeError("assigning non-exception to $!");
  1624. }
  1625. Debug.Assert(RubyUtils.GetVisibleException(e) == e);
  1626. return _currentException = e;
  1627. }
  1628. internal RubyArray GetCurrentExceptionBacktrace() {
  1629. // Under certain circumstances MRI invokes "backtrace" method, but it is quite unstable (crashes sometimes).
  1630. // Therefore we don't call the method and return the backtrace immediately.
  1631. Exception e = _currentException;
  1632. return (e != null) ? RubyExceptionData.GetInstance(e).Backtrace : null;
  1633. }
  1634. internal RubyArray SetCurrentExceptionBacktrace(object value) {
  1635. // first check availability of the current exception:
  1636. Exception e = _currentException;
  1637. if (e == null) {
  1638. throw RubyExceptions.CreateArgumentError("$! not set");
  1639. }
  1640. // check assigned value:
  1641. RubyArray array = RubyUtils.AsArrayOfStrings(value);
  1642. if (value != null && array == null) {
  1643. throw RubyExceptions.CreateTypeError("backtrace must be Array of String");
  1644. }
  1645. RubyExceptionData.GetInstance(e).Backtrace = array;
  1646. return array;
  1647. }
  1648. /// <summary>
  1649. /// $SAFE
  1650. /// </summary>
  1651. [ThreadStatic]
  1652. private static int _currentSafeLevel;
  1653. public int CurrentSafeLevel {
  1654. get { return _currentSafeLevel; }
  1655. }
  1656. public void SetSafeLevel(int value) {
  1657. if (_currentSafeLevel <= value) {
  1658. _currentSafeLevel = value;
  1659. } else {
  1660. throw RubyExceptions.CreateSecurityError(String.Format("tried to downgrade safe level from {0} to {1}",
  1661. _currentSafeLevel, value));
  1662. }
  1663. }
  1664. #endregion
  1665. #region Symbols
  1666. private readonly Dictionary<MutableString, RubySymbol>/*!*/ _symbols;
  1667. private object SymbolsLock { get { return _symbols; } }
  1668. public RubySymbol/*!*/ CreateSymbol(MutableString/*!*/ str) {
  1669. return CreateSymbol(str, true);
  1670. }
  1671. public RubySymbol/*!*/ CreateAsciiSymbol(string/*!*/ str) {
  1672. // TODO: do not allocate the MutableString if not needed?
  1673. return CreateSymbol(MutableString.CreateAscii(str), false);
  1674. }
  1675. public RubySymbol/*!*/ CreateSymbol(string/*!*/ str, RubyEncoding/*!*/ encoding) {
  1676. // TODO: do not allocate the MutableString if not needed?
  1677. return CreateSymbol(MutableString.CreateMutable(str, encoding), false);
  1678. }
  1679. public RubySymbol/*!*/ CreateSymbol(byte[]/*!*/ bytes, RubyEncoding/*!*/ encoding) {
  1680. var mstr = MutableString.CreateBinary(bytes, encoding);
  1681. // TODO: do not allocate the MutableString if not needed?
  1682. return CreateSymbol(mstr, false);
  1683. }
  1684. /// <summary>
  1685. /// Creates a symbol that holds on a given string or its copy, if <c>clone</c> is true.
  1686. /// Freezes the string the symbol holds on.
  1687. /// </summary>
  1688. public RubySymbol/*!*/ CreateSymbol(MutableString/*!*/ str, bool clone) {
  1689. RubySymbol result;
  1690. lock (SymbolsLock) {
  1691. if (!_symbols.TryGetValue(str, out result)) {
  1692. result = new RubySymbol((clone ? str.Clone() : str).Freeze(), _symbols.Count + RubySymbol.MinId, _runtimeId);
  1693. _symbols.Add(str, result);
  1694. }
  1695. }
  1696. return result;
  1697. }
  1698. /// <summary>
  1699. /// Searches symbol table for a symbol of given id - slow operation (linear search).
  1700. /// </summary>
  1701. public RubySymbol FindSymbol(int id) {
  1702. lock (SymbolsLock) {
  1703. foreach (var symbol in _symbols.Values) {
  1704. if (symbol.Id == id) {
  1705. return symbol;
  1706. }
  1707. }
  1708. }
  1709. return null;
  1710. }
  1711. public RubyArray/*!*/ GetAllSymbols() {
  1712. lock (SymbolsLock) {
  1713. return new RubyArray(_symbols.Values);
  1714. }
  1715. }
  1716. /// <summary>
  1717. /// TODO
  1718. /// Ruby 1.9 allows arbitrarily encoded identifiers. We could use a RubySymbol in internal tables, however that would also require
  1719. /// dynamic sites to use RubySymbols and CLR methods cached in the tables to be represented by RubySymbols. Seems like too much overhead.
  1720. ///
  1721. /// For now we take the same approach as with file system paths. We represent the identifiers as CLR strings and whenever we convert
  1722. /// them to RubySymbol or MutableString we use either the current KCODE or (K)UTF8 encoding. This doesn't guarantee a correct roundtrip
  1723. /// in the case that a method is defined under K-UTF8, its name is retrieved under K-SJIS, converted to a string and the bytes are examined.
  1724. /// The conversion might blow up if it contains a character that is not available in SJIS.
  1725. ///
  1726. /// <para>
  1727. /// Note that the way how 1.9 works makes non-ascii identifiers encoded by non-UTF-8 encoding almost useless.
  1728. /// Libraries written in such encoding are only usable from code written in the same encoding.
  1729. /// Thus the common case would probably be that all scripts use UTF-8. For example,
  1730. /// <code>
  1731. /// lib1.rb:
  1732. /// #encoding: UTF-8
  1733. /// class C; def S; end; end
  1734. ///
  1735. /// lib2.rb:
  1736. /// #encoding: SJIS
  1737. /// class D; def S; end; end
  1738. ///
  1739. /// c.rb:
  1740. /// #encoding: UTF-8
  1741. /// require 'lib1'
  1742. /// require 'lib2'
  1743. /// C.new.S # works
  1744. /// D.new.S # error: no method S
  1745. /// </code>
  1746. /// </para>
  1747. ///
  1748. /// <para>
  1749. /// Ruby 1.9 also allows incorrectly encoded method names (not identifiers in source code though):
  1750. /// <code>
  1751. /// #encoding: UTF-8
  1752. /// class C
  1753. /// define_method(:"foo\xce") { }
  1754. /// end
  1755. /// </code>
  1756. /// There seems to be no reason why we should support this.
  1757. /// </para>
  1758. /// </summary>
  1759. public RubyEncoding/*!*/ GetIdentifierEncoding() {
  1760. // TODO:
  1761. return RubyEncoding.UTF8;
  1762. }
  1763. public RubySymbol/*!*/ EncodeIdentifier(string/*!*/ identifier) {
  1764. return CreateSymbol(identifier, GetIdentifierEncoding());
  1765. }
  1766. /// <summary>
  1767. /// Returns an identifier encoded as MutableStrings (Ruby 1.8) or Symbols (Ruby 1.9).
  1768. /// </summary>
  1769. public object/*!*/ StringifyIdentifier(string/*!*/ identifier) {
  1770. // TODO:
  1771. return CreateSymbol(identifier, RubyEncoding.UTF8);
  1772. }
  1773. /// <summary>
  1774. /// Returns an array of identifiers encoded as MutableStrings (Ruby 1.8) or Symbols (Ruby 1.9).
  1775. /// </summary>
  1776. public RubyArray/*!*/ StringifyIdentifiers(IList<string>/*!*/ identifiers) {
  1777. var result = new RubyArray(identifiers.Count);
  1778. foreach (var id in identifiers) {
  1779. result.Add(StringifyIdentifier(id));
  1780. }
  1781. return result;
  1782. }
  1783. #endregion
  1784. #region IO (thread-safe)
  1785. private sealed class FileDescriptor {
  1786. public int DuplicateCount;
  1787. public readonly Stream/*!*/ Stream;
  1788. public FileDescriptor(Stream/*!*/ stream) {
  1789. Assert.NotNull(stream);
  1790. Stream = stream;
  1791. DuplicateCount = 1;
  1792. }
  1793. public void Close() {
  1794. DuplicateCount--;
  1795. if (DuplicateCount == 0) {
  1796. Stream.Close();
  1797. }
  1798. }
  1799. }
  1800. private readonly List<FileDescriptor>/*!*/ _fileDescriptors = new List<FileDescriptor>(10);
  1801. public const int StandardInputDescriptor = 0;
  1802. public const int StandardOutputDescriptor = 1;
  1803. public const int StandardErrorOutputDescriptor = 2;
  1804. public object StandardInput { get; set; }
  1805. public object StandardOutput { get; set; }
  1806. public object StandardErrorOutput { get; set; }
  1807. private FileDescriptor TryGetFileDescriptorNoLock(int descriptor) {
  1808. return (descriptor < 0 || descriptor >= _fileDescriptors.Count) ? null : _fileDescriptors[descriptor];
  1809. }
  1810. private int AddFileDescriptorNoLock(FileDescriptor/*!*/ fd) {
  1811. for (int i = 0; i < _fileDescriptors.Count; i++) {
  1812. if (_fileDescriptors[i] == null) {
  1813. _fileDescriptors[i] = fd;
  1814. return i;
  1815. }
  1816. }
  1817. _fileDescriptors.Add(fd);
  1818. return _fileDescriptors.Count - 1;
  1819. }
  1820. public Stream GetStream(int descriptor) {
  1821. lock (_fileDescriptors) {
  1822. var fd = TryGetFileDescriptorNoLock(descriptor);
  1823. return (fd != null) ? fd.Stream : null;
  1824. }
  1825. }
  1826. public void SetStream(int descriptor, Stream/*!*/ stream) {
  1827. ContractUtils.RequiresNotNull(stream, "stream");
  1828. lock (_fileDescriptors) {
  1829. var fd = TryGetFileDescriptorNoLock(descriptor);
  1830. if (fd == null) {
  1831. throw RubyExceptions.CreateEBADF();
  1832. }
  1833. if (fd.Stream != stream) {
  1834. fd.Close();
  1835. _fileDescriptors[descriptor] = new FileDescriptor(stream);
  1836. }
  1837. }
  1838. }
  1839. public void RedirectFileDescriptor(int descriptor, int toDescriptor) {
  1840. lock (_fileDescriptors) {
  1841. var fd = TryGetFileDescriptorNoLock(descriptor);
  1842. if (fd == null) {
  1843. throw RubyExceptions.CreateEBADF();
  1844. }
  1845. var toFd = TryGetFileDescriptorNoLock(toDescriptor);
  1846. if (toFd == null) {
  1847. throw RubyExceptions.CreateEBADF();
  1848. }
  1849. if (fd == toFd) {
  1850. return;
  1851. }
  1852. fd.Close();
  1853. toFd.DuplicateCount++;
  1854. _fileDescriptors[descriptor] = toFd;
  1855. }
  1856. }
  1857. public int AllocateFileDescriptor(Stream/*!*/ stream) {
  1858. ContractUtils.RequiresNotNull(stream, "stream");
  1859. lock (_fileDescriptors) {
  1860. return AddFileDescriptorNoLock(new FileDescriptor(stream));
  1861. }
  1862. }
  1863. public int DuplicateFileDescriptor(int descriptor) {
  1864. lock (_fileDescriptors) {
  1865. var fd = TryGetFileDescriptorNoLock(descriptor);
  1866. if (fd == null) {
  1867. throw RubyExceptions.CreateEBADF();
  1868. }
  1869. fd.DuplicateCount++;
  1870. return AddFileDescriptorNoLock(fd);
  1871. }
  1872. }
  1873. public void CloseStream(int descriptor) {
  1874. lock (_fileDescriptors) {
  1875. var fd = TryGetFileDescriptorNoLock(descriptor);
  1876. if (fd == null) {
  1877. throw RubyExceptions.CreateEBADF();
  1878. }
  1879. fd.Close();
  1880. _fileDescriptors[descriptor] = null;
  1881. }
  1882. }
  1883. public void RemoveFileDescriptor(int descriptor) {
  1884. lock (_fileDescriptors) {
  1885. if (TryGetFileDescriptorNoLock(descriptor) == null) {
  1886. throw RubyExceptions.CreateEBADF();
  1887. }
  1888. _fileDescriptors[descriptor] = null;
  1889. }
  1890. }
  1891. private readonly RuntimeErrorSink/*!*/ _runtimeErrorSink;
  1892. public RuntimeErrorSink/*!*/ RuntimeErrorSink {
  1893. get { return _runtimeErrorSink; }
  1894. }
  1895. public void ReportWarning(string/*!*/ message) {
  1896. ReportWarning(message, false);
  1897. }
  1898. public void ReportWarning(string/*!*/ message, bool isVerbose) {
  1899. _runtimeErrorSink.Add(null, message, SourceSpan.None, isVerbose ? Errors.RuntimeVerboseWarning : Errors.RuntimeWarning, Severity.Warning);
  1900. }
  1901. public RubyEncoding/*!*/ GetPathEncoding() {
  1902. return RubyEncoding.UTF8;
  1903. }
  1904. /// <summary>
  1905. /// Creates a mutable string encoded using the path (file system) encoding.
  1906. /// </summary>
  1907. /// <exception cref="EncoderFallbackException">Invalid characters present.</exception>
  1908. public MutableString/*!*/ EncodePath(string/*!*/ path) {
  1909. return EncodePath(path, GetPathEncoding());
  1910. }
  1911. public MutableString TryEncodePath(string/*!*/ path) {
  1912. var result = MutableString.Create(path, GetPathEncoding());
  1913. return result.ContainsInvalidCharacters() ? null : result;
  1914. }
  1915. internal static MutableString/*!*/ EncodePath(string/*!*/ path, RubyEncoding/*!*/ encoding) {
  1916. var result = MutableString.Create(path, encoding);
  1917. if (result.ContainsInvalidCharacters()) {
  1918. }
  1919. try {
  1920. return MutableString.Create(path, encoding).CheckEncoding();
  1921. } catch (EncoderFallbackException e) {
  1922. throw RubyExceptions.CreateEINVAL(
  1923. e,
  1924. "Path \"{0}\" contains characters that cannot be represented in encoding {1}: {2}",
  1925. path.ToAsciiString(),
  1926. encoding.Name,
  1927. e.Message
  1928. );
  1929. }
  1930. }
  1931. /// <summary>
  1932. /// Transcodes given mutable string to Unicode path that can be passed to the .NET IO system (or host).
  1933. /// </summary>
  1934. /// <exception cref="InvalidError">Invalid characters present.</exception>
  1935. public string/*!*/ DecodePath(MutableString/*!*/ path) {
  1936. try {
  1937. if (path.Encoding == RubyEncoding.Binary) {
  1938. // force UTF8 encoding to make round-trip work:
  1939. return path.ToString(Encoding.UTF8);
  1940. } else {
  1941. return path.ConvertToString();
  1942. }
  1943. } catch (DecoderFallbackException) {
  1944. throw RubyExceptions.CreateEINVAL("Invalid multi-byte sequence in path `{0}'", path.ToAsciiString());
  1945. }
  1946. }
  1947. #endregion
  1948. #region Library Data (thread-safe)
  1949. private Dictionary<object, object> _libraryData;
  1950. private void EnsureLibraryData() {
  1951. if (_libraryData == null) {
  1952. Interlocked.CompareExchange(ref _libraryData, new Dictionary<object, object>(), null);
  1953. }
  1954. }
  1955. public bool TryGetLibraryData(object key, out object value) {
  1956. EnsureLibraryData();
  1957. lock (_libraryData) {
  1958. return _libraryData.TryGetValue(key, out value);
  1959. }
  1960. }
  1961. public object GetOrCreateLibraryData(object key, Func<object> valueFactory) {
  1962. object value;
  1963. if (TryGetLibraryData(key, out value)) {
  1964. return value;
  1965. }
  1966. value = valueFactory();
  1967. object actualResult;
  1968. TryAddLibraryData(key, value, out actualResult);
  1969. return actualResult;
  1970. }
  1971. public bool TryAddLibraryData(object key, object value, out object actualValue) {
  1972. EnsureLibraryData();
  1973. lock (_libraryData) {
  1974. if (_libraryData.TryGetValue(key, out actualValue)) {
  1975. return false;
  1976. }
  1977. _libraryData.Add(key, actualValue = value);
  1978. return true;
  1979. }
  1980. }
  1981. public void TrySetLibraryData(object key, object value) {
  1982. EnsureLibraryData();
  1983. lock (_libraryData) {
  1984. _libraryData[key] = value;
  1985. }
  1986. }
  1987. #endregion
  1988. #region Parsing, Compilation (thread-safe)
  1989. public override ScriptCode CompileSourceCode(SourceUnit/*!*/ sourceUnit, CompilerOptions/*!*/ options, ErrorSink/*!*/ errorSink) {
  1990. ContractUtils.RequiresNotNull(sourceUnit, "sourceUnit");
  1991. ContractUtils.RequiresNotNull(options, "options");
  1992. ContractUtils.RequiresNotNull(errorSink, "errorSink");
  1993. ContractUtils.Requires(sourceUnit.LanguageContext == this, "Language mismatch.");
  1994. #if DEBUG
  1995. if (RubyOptions.LoadFromDisk) {
  1996. string code;
  1997. Utils.Log(String.Format("compiling {0}", sourceUnit.Path ??
  1998. ((code = sourceUnit.GetCode()).Length < 100 ? code : code.Substring(0, 100))
  1999. .Replace('\r', ' ').Replace('\n', ' ')
  2000. ), "COMPILER");
  2001. }
  2002. #endif
  2003. var rubyOptions = (RubyCompilerOptions)options;
  2004. var lambda = ParseSourceCode<Func<RubyScope, object, object>>(sourceUnit, rubyOptions, errorSink);
  2005. if (lambda == null) {
  2006. return null;
  2007. }
  2008. return new RubyScriptCode(lambda, sourceUnit, rubyOptions.FactoryKind);
  2009. }
  2010. #if MEASURE_AST
  2011. private static readonly object _TransformationLock = new object();
  2012. private static readonly Dictionary<ExpressionType, int> _TransformationHistogram = new Dictionary<ExpressionType,int>();
  2013. #endif
  2014. internal MSA.Expression<T> ParseSourceCode<T>(SourceUnit/*!*/ sourceUnit, RubyCompilerOptions/*!*/ options, ErrorSink/*!*/ errorSink) {
  2015. Debug.Assert(sourceUnit.LanguageContext == this);
  2016. SourceUnitTree ast = new Parser().Parse(sourceUnit, options, errorSink);
  2017. if (ast == null) {
  2018. return null;
  2019. }
  2020. MSA.Expression<T> lambda;
  2021. #if MEASURE_AST
  2022. lock (_TransformationLock) {
  2023. var oldHistogram = System.Linq.Expressions.Expression.Histogram;
  2024. System.Linq.Expressions.Expression.Histogram = _TransformationHistogram;
  2025. try {
  2026. #endif
  2027. lambda = TransformTree<T>(ast, sourceUnit, options);
  2028. #if MEASURE_AST
  2029. } finally {
  2030. System.Linq.Expressions.Expression.Histogram = oldHistogram;
  2031. }
  2032. }
  2033. #endif
  2034. return lambda;
  2035. }
  2036. internal MSA.Expression<T>/*!*/ TransformTree<T>(SourceUnitTree/*!*/ ast, SourceUnit/*!*/ sourceUnit, RubyCompilerOptions/*!*/ options) {
  2037. return ast.Transform<T>(
  2038. new AstGenerator(
  2039. this,
  2040. options,
  2041. sourceUnit.Document,
  2042. ast.Encoding,
  2043. sourceUnit.Kind == SourceCodeKind.InteractiveCode
  2044. )
  2045. );
  2046. }
  2047. public override CompilerOptions/*!*/ GetCompilerOptions() {
  2048. return new RubyCompilerOptions(_options) {
  2049. FactoryKind = TopScopeFactoryKind.Hosted,
  2050. };
  2051. }
  2052. public override CompilerOptions/*!*/ GetCompilerOptions(Scope/*!*/ scope) {
  2053. var result = new RubyCompilerOptions(_options) {
  2054. FactoryKind = TopScopeFactoryKind.Hosted
  2055. };
  2056. var rubyGlobalScope = (RubyGlobalScope)scope.GetExtension(ContextId);
  2057. if (rubyGlobalScope != null && rubyGlobalScope.TopLocalScope != null) {
  2058. result.LocalNames = rubyGlobalScope.TopLocalScope.GetVisibleLocalNames();
  2059. }
  2060. return result;
  2061. }
  2062. public override ErrorSink GetCompilerErrorSink() {
  2063. return _runtimeErrorSink;
  2064. }
  2065. public override ScriptCode/*!*/ LoadCompiledCode(Delegate/*!*/ method, string path, string customData) {
  2066. // TODO: we need to save the kind of the scope factory:
  2067. SourceUnit su = new SourceUnit(this, NullTextContentProvider.Null, path, SourceCodeKind.File);
  2068. return new RubyScriptCode((Func<RubyScope, object, object>)method, su, TopScopeFactoryKind.Hosted);
  2069. }
  2070. #endregion
  2071. #region Global Scope (thread-safe)
  2072. /// <summary>
  2073. /// Creates a scope extension for a DLR scope unless it already exists for the given scope.
  2074. /// </summary>
  2075. internal RubyGlobalScope/*!*/ InitializeGlobalScope(Scope/*!*/ globalScope, bool createHosted, bool bindGlobals) {
  2076. Assert.NotNull(globalScope);
  2077. var scopeExtension = globalScope.GetExtension(ContextId);
  2078. if (scopeExtension != null) {
  2079. return (RubyGlobalScope)scopeExtension;
  2080. }
  2081. RubyObject mainObject = new RubyObject(_objectClass);
  2082. RubyClass mainSingleton = GetOrCreateMainSingleton(mainObject, null);
  2083. RubyGlobalScope result = new RubyGlobalScope(this, globalScope, mainObject, createHosted);
  2084. if (bindGlobals) {
  2085. mainSingleton.SetMethodNoEvent(this, Symbols.MethodMissing, new RubyScopeMethodMissingInfo(RubyMemberFlags.Private, mainSingleton));
  2086. mainSingleton.SetGlobalScope(result);
  2087. }
  2088. return (RubyGlobalScope)globalScope.SetExtension(ContextId, result);
  2089. }
  2090. public override int ExecuteProgram(SourceUnit/*!*/ program) {
  2091. try {
  2092. RubyCompilerOptions options = new RubyCompilerOptions(_options) {
  2093. FactoryKind = TopScopeFactoryKind.Main
  2094. };
  2095. CompileSourceCode(program, options, _runtimeErrorSink).Run();
  2096. } catch (SystemExit e) {
  2097. return e.Status;
  2098. }
  2099. return 0;
  2100. }
  2101. #endregion
  2102. #region Shutdown (thread-safe)
  2103. private readonly List<Proc> _shutdownHandlers = new List<Proc>();
  2104. private object ShutdownHandlersLock { get { return _shutdownHandlers; }}
  2105. public void RegisterShutdownHandler(Proc/*!*/ proc) {
  2106. ContractUtils.RequiresNotNull(proc, "proc");
  2107. lock (ShutdownHandlersLock) {
  2108. _shutdownHandlers.Add(proc);
  2109. }
  2110. }
  2111. private void ExecuteShutdownHandlers() {
  2112. SystemExit lastSystemExit = null;
  2113. Exception lastException = null;
  2114. while (true) {
  2115. Proc[] handlers;
  2116. lock (ShutdownHandlersLock) {
  2117. if (_shutdownHandlers.Count == 0) {
  2118. break;
  2119. }
  2120. handlers = _shutdownHandlers.ToReverseArray();
  2121. _shutdownHandlers.Clear();
  2122. }
  2123. foreach (var handler in handlers) {
  2124. try {
  2125. handler.Call(null);
  2126. } catch (SystemExit e) {
  2127. // Kernel#at_exit can call exit and set the exitcode. Furthermore, exit can be called
  2128. // from multiple blocks registered with Kernel#at_exit.
  2129. lastSystemExit = e;
  2130. } catch (Exception e) {
  2131. CurrentException = e;
  2132. lastException = e;
  2133. // TODO: GetIdentifierEncoding
  2134. _runtimeErrorSink.WriteMessage(MutableString.CreateMutable(FormatException(e), RubyEncoding.UTF8));
  2135. }
  2136. }
  2137. }
  2138. if (lastSystemExit != null) {
  2139. throw lastSystemExit;
  2140. } else if (lastException != null) {
  2141. // at least one unhandled exception:
  2142. throw new SystemExit(1);
  2143. }
  2144. }
  2145. public override void Shutdown() {
  2146. #if !SILVERLIGHT
  2147. _upTime.Stop();
  2148. if (RubyOptions.Profile) {
  2149. var profile = Profiler.Instance.GetProfile();
  2150. using (TextWriter writer = File.CreateText("profile.log")) {
  2151. int maxLength = 0;
  2152. long totalTicks = 0L;
  2153. var keys = new string[profile.Count];
  2154. var values = new long[profile.Count];
  2155. int i = 0;
  2156. foreach (var counter in profile) {
  2157. string methodInfo = counter.Id;
  2158. if (methodInfo.Length > maxLength) {
  2159. maxLength = methodInfo.Length;
  2160. }
  2161. totalTicks += counter.Ticks;
  2162. keys[i] = methodInfo;
  2163. values[i] = counter.Ticks;
  2164. i++;
  2165. }
  2166. Array.Sort(values, keys);
  2167. for (int j = keys.Length - 1; j >= 0; j--) {
  2168. long ticks = values[j];
  2169. writer.WriteLine("{0,-" + (maxLength + 4) + "} {1,8:F0} ms {2,5:F1}%", keys[j],
  2170. new TimeSpan(Utils.DateTimeTicksFromStopwatch(ticks)).TotalMilliseconds,
  2171. (((double)ticks) / totalTicks * 100)
  2172. );
  2173. }
  2174. writer.WriteLine("{0,-" + (maxLength + 4) + "} {1,8:F0} ms", "total",
  2175. new TimeSpan(Utils.DateTimeTicksFromStopwatch(totalTicks)).TotalMilliseconds
  2176. );
  2177. }
  2178. }
  2179. if (Options.PerfStats) {
  2180. using (TextWriter output = File.CreateText("perfstats.log")) {
  2181. output.WriteLine(String.Format(@"
  2182. total: {0}
  2183. binding: {1} ({2} calls)
  2184. ",
  2185. _upTime.Elapsed,
  2186. #if MEASURE
  2187. new TimeSpan(MetaAction.BindingTimeTicks),
  2188. MetaAction.BindCallCount
  2189. #else
  2190. "N/A", "N/A"
  2191. #endif
  2192. ));
  2193. #if MEASURE_BINDING
  2194. output.WriteLine();
  2195. output.WriteLine("---- MetaAction kinds ----");
  2196. output.WriteLine();
  2197. PerfTrack.DumpHistogram(MetaAction.HistogramOfKinds, output);
  2198. output.WriteLine();
  2199. output.WriteLine();
  2200. output.WriteLine("---- MetaAction instances ----");
  2201. output.WriteLine();
  2202. PerfTrack.DumpHistogram(MetaAction.HistogramOfInstances, output);
  2203. output.WriteLine();
  2204. #endif
  2205. #if MEASURE_AST
  2206. output.WriteLine();
  2207. output.WriteLine("---- Ruby Parser generated Expression Trees ----");
  2208. output.WriteLine();
  2209. PerfTrack.DumpHistogram(_TransformationHistogram, output);
  2210. output.WriteLine();
  2211. #endif
  2212. PerfTrack.DumpStats(output);
  2213. }
  2214. }
  2215. #endif
  2216. _loader.SaveCompiledCode();
  2217. ExecuteShutdownHandlers();
  2218. _currentException = null;
  2219. }
  2220. #endregion
  2221. #region Exceptions (thread-safe)
  2222. /// <summary>
  2223. /// Formats exceptions like Ruby does.
  2224. /// </summary>
  2225. /// <remarks>
  2226. /// For example,
  2227. /// <code>
  2228. /// repro.rb:2:in `fetch': wrong number of arguments (0 for 1) (ArgumentError)
  2229. /// from repro.rb:2:in `test'
  2230. /// from repro.rb:5
  2231. /// </code>
  2232. /// </remarks>
  2233. public override string/*!*/ FormatException(Exception/*!*/ exception) {
  2234. var syntaxError = exception as SyntaxError;
  2235. if (syntaxError != null && syntaxError.HasLineInfo) {
  2236. return FormatErrorMessage(syntaxError.Message, null, syntaxError.File, syntaxError.Line, syntaxError.Column, syntaxError.LineSourceCode);
  2237. }
  2238. var exceptionClass = GetClassOf(exception);
  2239. RubyExceptionData data = RubyExceptionData.GetInstance(exception);
  2240. string message = RubyExceptionData.GetClrMessage(this, data.Message);
  2241. RubyArray backtrace = data.Backtrace;
  2242. StringBuilder sb = new StringBuilder();
  2243. if (backtrace != null && backtrace.Count > 0) {
  2244. sb.AppendFormat("{0}: {1} ({2})", Protocols.ToClrStringNoThrow(this, backtrace[0]), message, exceptionClass.Name);
  2245. sb.AppendLine();
  2246. for (int i = 1; i < backtrace.Count; i++) {
  2247. sb.Append("\tfrom ").Append(Protocols.ToClrStringNoThrow(this, backtrace[i])).AppendLine();
  2248. }
  2249. } else {
  2250. sb.AppendFormat("unknown: {0} ({1})", message, exceptionClass.Name).AppendLine();
  2251. }
  2252. // display the raw CLR exception & strack trace if requested
  2253. if (Options.ShowClrExceptions) {
  2254. sb.AppendLine().AppendLine();
  2255. sb.AppendLine("CLR exception:");
  2256. sb.Append(base.FormatException(exception));
  2257. sb.AppendLine();
  2258. }
  2259. return sb.ToString();
  2260. }
  2261. internal static string/*!*/ FormatErrorMessage(string/*!*/ message, string prefix, string file, int line, int column, string lineSource) {
  2262. var sb = new StringBuilder();
  2263. sb.Append(file ?? "unknown");
  2264. sb.Append(':');
  2265. sb.Append(file != null ? line : 0);
  2266. sb.Append(": ");
  2267. if (prefix != null) {
  2268. sb.Append(prefix);
  2269. sb.Append(": ");
  2270. }
  2271. sb.Append(message);
  2272. sb.AppendLine();
  2273. if (lineSource != null) {
  2274. sb.Append(lineSource);
  2275. sb.AppendLine();
  2276. if (column > 0) {
  2277. sb.Append(' ', column - 1);
  2278. sb.Append('^');
  2279. sb.AppendLine();
  2280. }
  2281. }
  2282. return sb.ToString();
  2283. }
  2284. public Action InterruptSignalHandler { get; set; }
  2285. #endregion
  2286. #region Language Context Overrides
  2287. public override TService GetService<TService>(params object[] args) {
  2288. if (typeof(TService) == typeof(RubyService)) {
  2289. return (TService)(object)(_rubyService ?? (_rubyService = new RubyService(this, (Microsoft.Scripting.Hosting.ScriptEngine)args[0])));
  2290. }
  2291. if (typeof(TService) == typeof(TokenizerService)) {
  2292. return (TService)(object)new Tokenizer();
  2293. }
  2294. return base.GetService<TService>(args);
  2295. }
  2296. public override void SetSearchPaths(ICollection<string/*!*/>/*!*/ paths) {
  2297. ContractUtils.RequiresNotNullItems(paths, "paths");
  2298. _loader.SetLoadPaths(paths);
  2299. }
  2300. // Might run an arbitrary user code.
  2301. public override ICollection<string>/*!*/ GetSearchPaths() {
  2302. return _loader.GetLoadPathStrings();
  2303. }
  2304. public override SourceCodeReader/*!*/ GetSourceReader(Stream/*!*/ stream, Encoding/*!*/ defaultEncoding, string path) {
  2305. ContractUtils.RequiresNotNull(stream, "stream");
  2306. ContractUtils.RequiresNotNull(defaultEncoding, "defaultEncoding");
  2307. ContractUtils.Requires(stream.CanRead && stream.CanSeek, "stream", "The stream must support seeking and reading");
  2308. #if SILVERLIGHT
  2309. return base.GetSourceReader(stream, defaultEncoding, path);
  2310. #else
  2311. return GetSourceReader(stream, defaultEncoding);
  2312. }
  2313. private SourceCodeReader/*!*/ GetSourceReader(Stream/*!*/ stream, Encoding/*!*/ defaultEncoding) {
  2314. long initialPosition = stream.Position;
  2315. var reader = new StreamReader(stream, BinaryEncoding.Instance, true);
  2316. // reads preamble, if present:
  2317. reader.Peek();
  2318. Encoding preambleEncoding = (reader.CurrentEncoding != BinaryEncoding.Instance) ? reader.CurrentEncoding : null;
  2319. Encoding rubyPreambleEncoding = null;
  2320. // header:
  2321. string encodingName;
  2322. if (Tokenizer.TryParseEncodingHeader(reader, out encodingName)) {
  2323. rubyPreambleEncoding = GetEncodingByRubyName(encodingName);
  2324. // Check if the preamble encoding is an identity on preamble bytes.
  2325. // If not we shouldn't allow such encoding since the encoding of the preamble would be different from the encoding of the file.
  2326. if (!RubyEncoding.AsciiIdentity(rubyPreambleEncoding)) {
  2327. throw new IOException(String.Format("Encoding '{0}' is not allowed in preamble.", rubyPreambleEncoding.WebName));
  2328. }
  2329. }
  2330. // skip the encoding preamble, the resulting stream shouldn't detect the preamble again
  2331. // (this is necessary to override the preamble by Ruby specific one):
  2332. if (preambleEncoding != null) {
  2333. initialPosition += preambleEncoding.GetPreamble().Length;
  2334. }
  2335. stream.Seek(initialPosition, SeekOrigin.Begin);
  2336. var encoding = rubyPreambleEncoding ?? preambleEncoding ?? defaultEncoding;
  2337. return new SourceCodeReader(new StreamReader(stream, encoding, false), encoding);
  2338. #endif
  2339. }
  2340. /// <exception cref="ArgumentException">Unknown encoding.</exception>
  2341. public Encoding/*!*/ GetEncodingByRubyName(string/*!*/ name) {
  2342. ContractUtils.RequiresNotNull(name, "name");
  2343. var upperName = name.ToUpperInvariant();
  2344. switch (upperName) {
  2345. case "BINARY":
  2346. case "ASCII-8BIT": return BinaryEncoding.Instance;
  2347. case "FILESYSTEM": return GetPathEncoding().StrictEncoding;
  2348. case "LOCALE": return _options.LocaleEncoding.StrictEncoding;
  2349. case "EXTERNAL": return _defaultExternalEncoding.StrictEncoding;
  2350. #if SILVERLIGHT
  2351. case "UTF-8": return Encoding.UTF8;
  2352. default: throw new ArgumentException(String.Format("Unknown encoding: '{0}'", name));
  2353. #else
  2354. // Mono doesn't recognize 'SJIS' encoding name:
  2355. case "SJIS": return Encoding.GetEncoding(RubyEncoding.CodePageSJIS);
  2356. case "WINDOWS-31J": return Encoding.GetEncoding(932);
  2357. case "MACCYRILLIC": return Encoding.GetEncoding(10007);
  2358. // encodings whose name only differs in casing are returned by Windows:
  2359. case "EUC-JP": return Encoding.GetEncoding(RubyEncoding.CodePageEUCJP);
  2360. case "ISO-2022-JP": return Encoding.GetEncoding(50220);
  2361. // the encoding name doesn't correspond to its code page:
  2362. case "CP1025": return Encoding.GetEncoding(21025);
  2363. default:
  2364. string alias;
  2365. if (RubyEncoding.Aliases.TryGetValue(name, out alias)) {
  2366. return GetEncodingByRubyName(alias);
  2367. }
  2368. if (upperName.StartsWith("CP", StringComparison.Ordinal)) {
  2369. int codepage;
  2370. if (Int32.TryParse(upperName.Substring(2), out codepage)) {
  2371. try {
  2372. return Encoding.GetEncoding(codepage);
  2373. } catch (NotSupportedException) {
  2374. // the encoding name is not correct
  2375. }
  2376. }
  2377. }
  2378. return Encoding.GetEncoding(name);
  2379. #endif
  2380. }
  2381. }
  2382. /// <exception cref="ArgumentException">Unknown encoding.</exception>
  2383. public RubyEncoding/*!*/ GetRubyEncoding(MutableString/*!*/ name) {
  2384. if (!name.IsAscii()) {
  2385. throw new ArgumentException(String.Format("Unknown encoding: '{0}'", name.ToAsciiString()));
  2386. }
  2387. return RubyEncoding.GetRubyEncoding(GetEncodingByRubyName(name.ToString()));
  2388. }
  2389. /// <exception cref="ArgumentException">Unknown encoding.</exception>
  2390. public RubyEncoding/*!*/ GetRubyEncoding(string/*!*/ name) {
  2391. return RubyEncoding.GetRubyEncoding(GetEncodingByRubyName(name));
  2392. }
  2393. public override string/*!*/ FormatObject(DynamicOperations/*!*/ operations, object obj) {
  2394. var inspectSite = operations.GetOrCreateSite<object, object>(
  2395. RubyCallAction.Make(this, "inspect", RubyCallSignature.WithImplicitSelf(1))
  2396. );
  2397. var tosSite = operations.GetOrCreateSite<object, MutableString>(ConvertToSAction.Make(this));
  2398. return tosSite.Target(tosSite, inspectSite.Target(inspectSite, obj)).ToString();
  2399. }
  2400. #endregion
  2401. #region MetaObject binding
  2402. public override GetMemberBinder/*!*/ CreateGetMemberBinder(string/*!*/ name, bool ignoreCase) {
  2403. // TODO:
  2404. if (ignoreCase) {
  2405. return base.CreateGetMemberBinder(name, ignoreCase);
  2406. }
  2407. return _metaBinderFactory.InteropGetMember(name);
  2408. }
  2409. public override SetMemberBinder/*!*/ CreateSetMemberBinder(string name, bool ignoreCase) {
  2410. // TODO:
  2411. if (ignoreCase) {
  2412. return base.CreateSetMemberBinder(name, ignoreCase);
  2413. }
  2414. // TODO: name mangling
  2415. return _metaBinderFactory.InteropSetMemberExact(name);
  2416. }
  2417. public override InvokeMemberBinder/*!*/ CreateCallBinder(string/*!*/ name, bool ignoreCase, CallInfo/*!*/ callInfo) {
  2418. // TODO:
  2419. if (ignoreCase || callInfo.ArgumentNames.Count != 0) {
  2420. return base.CreateCallBinder(name, ignoreCase, callInfo);
  2421. }
  2422. return _metaBinderFactory.InteropInvokeMember(name, callInfo);
  2423. }
  2424. public override CreateInstanceBinder/*!*/ CreateCreateBinder(CallInfo/*!*/ callInfo) {
  2425. // TODO:
  2426. if (callInfo.ArgumentNames.Count != 0) {
  2427. return base.CreateCreateBinder(callInfo);
  2428. }
  2429. return _metaBinderFactory.InteropCreateInstance(callInfo);
  2430. }
  2431. public override ConvertBinder/*!*/ CreateConvertBinder(Type toType, bool? explicitCast) {
  2432. return _metaBinderFactory.InteropConvert(toType, explicitCast ?? true);
  2433. }
  2434. // TODO: override GetMemberNames?
  2435. public IList<string>/*!*/ GetForeignDynamicMemberNames(object obj) {
  2436. if (obj is IRubyDynamicMetaObjectProvider) {
  2437. return ArrayUtils.EmptyStrings;
  2438. }
  2439. #if !SILVERLIGHT // COM
  2440. if (TypeUtils.IsComObject(obj)) {
  2441. return new List<string>(Microsoft.Scripting.ComInterop.ComBinder.GetDynamicMemberNames(obj));
  2442. }
  2443. #endif
  2444. return GetMemberNames(obj);
  2445. }
  2446. #endregion
  2447. #region Dynamic Sites (thread-safe)
  2448. private CallSite<Func<CallSite, object, MutableString>> _stringConversionSite;
  2449. public CallSite<Func<CallSite, object, MutableString>>/*!*/ StringConversionSite {
  2450. get { return RubyUtils.GetCallSite(ref _stringConversionSite, ConvertToSAction.Make(this)); }
  2451. }
  2452. private readonly Dictionary<Key<string, RubyCallSignature>, CallSite>/*!*/ _sendSites =
  2453. new Dictionary<Key<string, RubyCallSignature>, CallSite>();
  2454. private object SendSitesLock { get { return _sendSites; } }
  2455. public CallSite<TSiteFunc>/*!*/ GetOrCreateSendSite<TSiteFunc>(string/*!*/ methodName, RubyCallSignature callSignature)
  2456. where TSiteFunc : class {
  2457. lock (SendSitesLock) {
  2458. CallSite site;
  2459. if (_sendSites.TryGetValue(Key.Create(methodName, callSignature), out site)) {
  2460. return (CallSite<TSiteFunc>)site;
  2461. }
  2462. var newSite = CallSite<TSiteFunc>.Create(RubyCallAction.Make(this, methodName, callSignature));
  2463. _sendSites.Add(Key.Create(methodName, callSignature), newSite);
  2464. return newSite;
  2465. }
  2466. }
  2467. public DynamicDelegateCreator/*!*/ DelegateCreator {
  2468. get {
  2469. if (_delegateCreator == null) {
  2470. Interlocked.CompareExchange(ref _delegateCreator, new DynamicDelegateCreator(this), null);
  2471. }
  2472. return _delegateCreator;
  2473. }
  2474. }
  2475. #endregion
  2476. #region Ruby Events
  2477. private CallSite<Func<CallSite, object, object, object>> _respondTo;
  2478. internal object Send(ref CallSite<Func<CallSite, object, object, object>> site, string/*!*/ eventName,
  2479. object target, string/*!*/ memberName) {
  2480. if (site == null) {
  2481. Interlocked.CompareExchange(
  2482. ref site,
  2483. CallSite<Func<CallSite, object, object, object>>.Create(RubyCallAction.Make(this, eventName, RubyCallSignature.WithImplicitSelf(1))),
  2484. null
  2485. );
  2486. }
  2487. return site.Target(site, target, EncodeIdentifier(memberName));
  2488. }
  2489. public bool RespondTo(object target, string/*!*/ methodName) {
  2490. return RubyOps.IsTrue(Send(ref _respondTo, "respond_to?", target, methodName));
  2491. }
  2492. internal void ReportTraceEvent(string/*!*/ operation, RubyScope/*!*/ scope, RubyModule/*!*/ module, string/*!*/ name, string fileName, int lineNumber) {
  2493. if (_traceListener != null && !_traceListenerSuspended) {
  2494. try {
  2495. _traceListenerSuspended = true;
  2496. _traceListener.Call(null, new[] {
  2497. MutableString.CreateAscii(operation), // event
  2498. fileName != null ? scope.RubyContext.EncodePath(fileName) : null, // file
  2499. ScriptingRuntimeHelpers.Int32ToObject(lineNumber), // line
  2500. EncodeIdentifier(name), // TODO: alias
  2501. new Binding(scope), // binding
  2502. module.IsSingletonClass ? ((RubyClass)module).SingletonClassOf : module // module
  2503. });
  2504. } finally {
  2505. _traceListenerSuspended = false;
  2506. }
  2507. }
  2508. }
  2509. #endregion
  2510. }
  2511. }