PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/IronPython_Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs

#
C# | 2570 lines | 1954 code | 449 blank | 167 comment | 303 complexity | 59b560ffd38a41d8fd908eeb89bd675a MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0

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

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using MSA = System.Linq.Expressions;
  17. #else
  18. using MSA = Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using System.Dynamic;
  25. using System.Globalization;
  26. using System.IO;
  27. using System.Reflection;
  28. using System.Runtime.CompilerServices;
  29. using System.Runtime.Serialization;
  30. using System.Threading;
  31. using IronRuby.Builtins;
  32. using IronRuby.Compiler;
  33. using IronRuby.Compiler.Generation;
  34. using IronRuby.Runtime.Calls;
  35. using Microsoft.Scripting;
  36. using Microsoft.Scripting.Interpreter;
  37. using Microsoft.Scripting.Math;
  38. using Microsoft.Scripting.Runtime;
  39. using Microsoft.Scripting.Utils;
  40. using IronRuby.Compiler.Ast;
  41. using IronRuby.Runtime.Conversions;
  42. namespace IronRuby.Runtime {
  43. [ReflectionCached, CLSCompliant(false)]
  44. public static partial class RubyOps {
  45. [Emitted]
  46. public static readonly object DefaultArgument = new object();
  47. // Returned by a virtual site if a base call should be performed.
  48. [Emitted]
  49. public static readonly object ForwardToBase = new object();
  50. // an instance of a dummy type that causes any rule based on instance type check to fail
  51. private sealed class _NeedsUpdate {
  52. }
  53. [Emitted]
  54. public static readonly object NeedsUpdate = new _NeedsUpdate();
  55. #region Scopes
  56. [Emitted]
  57. public static MutableTuple GetLocals(RubyScope/*!*/ scope) {
  58. return scope.Locals;
  59. }
  60. [Emitted]
  61. public static MutableTuple GetParentLocals(RubyScope/*!*/ scope) {
  62. return scope.Parent.Locals;
  63. }
  64. [Emitted]
  65. public static RubyScope/*!*/ GetParentScope(RubyScope/*!*/ scope) {
  66. return scope.Parent;
  67. }
  68. [Emitted]
  69. public static Proc GetMethodBlockParameter(RubyScope/*!*/ scope) {
  70. var methodScope = scope.GetInnerMostMethodScope();
  71. return methodScope != null ? methodScope.BlockParameter : null;
  72. }
  73. [Emitted]
  74. public static object GetMethodBlockParameterSelf(RubyScope/*!*/ scope) {
  75. Proc proc = scope.GetInnerMostMethodScope().BlockParameter;
  76. Debug.Assert(proc != null, "CreateBfcForYield is called before this method and it checks non-nullity");
  77. return proc.Self;
  78. }
  79. [Emitted]
  80. public static object GetProcSelf(Proc/*!*/ proc) {
  81. return proc.Self;
  82. }
  83. [Emitted]
  84. public static int GetProcArity(Proc/*!*/ proc) {
  85. return proc.Dispatcher.Arity;
  86. }
  87. [Emitted]
  88. public static void InitializeScope(RubyScope/*!*/ scope, MutableTuple locals, string[] variableNames,
  89. InterpretedFrame interpretedFrame) {
  90. if (!scope.LocalsInitialized) {
  91. scope.SetLocals(locals, variableNames ?? ArrayUtils.EmptyStrings);
  92. }
  93. scope.InterpretedFrame = interpretedFrame;
  94. }
  95. [Emitted]
  96. public static void InitializeScopeNoLocals(RubyScope/*!*/ scope, InterpretedFrame interpretedFrame) {
  97. scope.InterpretedFrame = interpretedFrame;
  98. }
  99. [Emitted]
  100. public static void SetDataConstant(RubyScope/*!*/ scope, string/*!*/ dataPath, int dataOffset) {
  101. Debug.Assert(dataOffset >= 0);
  102. RubyFile dataFile;
  103. RubyContext context = scope.RubyContext;
  104. if (context.DomainManager.Platform.FileExists(dataPath)) {
  105. dataFile = new RubyFile(context, dataPath, IOMode.ReadOnly);
  106. dataFile.Seek(dataOffset, SeekOrigin.Begin);
  107. } else {
  108. dataFile = null;
  109. }
  110. context.ObjectClass.SetConstant("DATA", dataFile);
  111. }
  112. [Emitted]
  113. public static RubyModuleScope/*!*/ CreateModuleScope(MutableTuple locals, string[] variableNames,
  114. RubyScope/*!*/ parent, RubyModule/*!*/ module) {
  115. if (parent.RubyContext != module.Context) {
  116. throw RubyExceptions.CreateTypeError("Cannot open a module `{0}' defined in a foreign runtime #{1}", module.Name, module.Context.RuntimeId);
  117. }
  118. RubyModuleScope scope = new RubyModuleScope(parent, module);
  119. scope.SetDebugName((module.IsClass ? "class" : "module") + " " + module.Name);
  120. scope.SetLocals(locals, variableNames ?? ArrayUtils.EmptyStrings);
  121. return scope;
  122. }
  123. [Emitted]
  124. public static RubyMethodScope/*!*/ CreateMethodScope(MutableTuple locals, string[] variableNames, int visibleParameterCount,
  125. RubyScope/*!*/ parentScope, RubyModule/*!*/ declaringModule, string/*!*/ definitionName,
  126. object selfObject, Proc blockParameter, InterpretedFrame interpretedFrame) {
  127. return new RubyMethodScope(
  128. locals, variableNames ?? ArrayUtils.EmptyStrings, visibleParameterCount,
  129. parentScope, declaringModule, definitionName, selfObject, blockParameter,
  130. interpretedFrame
  131. );
  132. }
  133. [Emitted]
  134. public static RubyScope/*!*/ CreateFileInitializerScope(MutableTuple locals, string[] variableNames, RubyScope/*!*/ parent) {
  135. return new RubyFileInitializerScope(locals, variableNames ?? ArrayUtils.EmptyStrings, parent);
  136. }
  137. [Emitted]
  138. public static RubyBlockScope/*!*/ CreateBlockScope(MutableTuple locals, string[] variableNames,
  139. BlockParam/*!*/ blockParam, object selfObject, InterpretedFrame interpretedFrame) {
  140. return new RubyBlockScope(locals, variableNames ?? ArrayUtils.EmptyStrings, blockParam, selfObject, interpretedFrame);
  141. }
  142. [Emitted]
  143. public static void TraceMethodCall(RubyMethodScope/*!*/ scope, string fileName, int lineNumber) {
  144. // MRI:
  145. // Reports DeclaringModule even though an aliased method in a sub-module is called.
  146. // Also works for singleton module-function, which shares DeclaringModule with instance module-function.
  147. RubyModule module = scope.DeclaringModule;
  148. scope.RubyContext.ReportTraceEvent("call", scope, module, scope.DefinitionName, fileName, lineNumber);
  149. }
  150. [Emitted]
  151. public static void TraceMethodReturn(RubyMethodScope/*!*/ scope, string fileName, int lineNumber) {
  152. RubyModule module = scope.DeclaringModule;
  153. scope.RubyContext.ReportTraceEvent("return", scope, module, scope.DefinitionName, fileName, lineNumber);
  154. }
  155. [Emitted]
  156. public static void TraceBlockCall(RubyBlockScope/*!*/ scope, BlockParam/*!*/ block, string fileName, int lineNumber) {
  157. var method = block.Proc.Method;
  158. if (method != null) {
  159. scope.RubyContext.ReportTraceEvent("call", scope, method.DeclaringModule, method.DefinitionName, fileName, lineNumber);
  160. }
  161. }
  162. [Emitted]
  163. public static void TraceBlockReturn(RubyBlockScope/*!*/ scope, BlockParam/*!*/ block, string fileName, int lineNumber) {
  164. var method = block.Proc.Method;
  165. if (method != null) {
  166. scope.RubyContext.ReportTraceEvent("return", scope, method.DeclaringModule, method.DefinitionName, fileName, lineNumber);
  167. }
  168. }
  169. [Emitted]
  170. public static void PrintInteractiveResult(RubyScope/*!*/ scope, MutableString/*!*/ value) {
  171. var writer = scope.RubyContext.DomainManager.SharedIO.OutputStream;
  172. writer.WriteByte((byte)'=');
  173. writer.WriteByte((byte)'>');
  174. writer.WriteByte((byte)' ');
  175. var bytes = value.ToByteArray();
  176. writer.Write(bytes, 0, bytes.Length);
  177. writer.WriteByte((byte)'\r');
  178. writer.WriteByte((byte)'\n');
  179. }
  180. [Emitted]
  181. public static object GetLocalVariable(RubyScope/*!*/ scope, string/*!*/ name) {
  182. return scope.ResolveLocalVariable(name);
  183. }
  184. [Emitted]
  185. public static object SetLocalVariable(object value, RubyScope/*!*/ scope, string/*!*/ name) {
  186. return scope.ResolveAndSetLocalVariable(name, value);
  187. }
  188. [Emitted]
  189. public static VersionHandle/*!*/ GetSelfClassVersionHandle(RubyScope/*!*/ scope) {
  190. return scope.SelfImmediateClass.Version;
  191. }
  192. #endregion
  193. #region Context
  194. [Emitted]
  195. public static RubyContext/*!*/ GetContextFromModule(RubyModule/*!*/ module) {
  196. return module.Context;
  197. }
  198. [Emitted]
  199. public static RubyContext/*!*/ GetContextFromIRubyObject(IRubyObject/*!*/ obj) {
  200. return obj.ImmediateClass.Context;
  201. }
  202. [Emitted]
  203. public static RubyContext/*!*/ GetContextFromScope(RubyScope/*!*/ scope) {
  204. return scope.RubyContext;
  205. }
  206. [Emitted]
  207. public static RubyContext/*!*/ GetContextFromMethod(RubyMethod/*!*/ method) {
  208. return method.Info.Context;
  209. }
  210. [Emitted]
  211. public static RubyContext/*!*/ GetContextFromBlockParam(BlockParam/*!*/ block) {
  212. return block.RubyContext;
  213. }
  214. [Emitted]
  215. public static RubyContext/*!*/ GetContextFromProc(Proc/*!*/ proc) {
  216. return proc.LocalScope.RubyContext;
  217. }
  218. [Emitted]
  219. public static RubyScope/*!*/ GetEmptyScope(RubyContext/*!*/ context) {
  220. return context.EmptyScope;
  221. }
  222. [Emitted]
  223. public static Scope/*!*/ GetGlobalScopeFromScope(RubyScope/*!*/ scope) {
  224. return scope.GlobalScope.Scope;
  225. }
  226. #endregion
  227. #region Blocks
  228. [Emitted]
  229. public static Proc InstantiateBlock(RubyScope/*!*/ scope, object self, BlockDispatcher/*!*/ dispatcher) {
  230. return (dispatcher.Method != null) ? new Proc(ProcKind.Block, self, scope, dispatcher) : null;
  231. }
  232. [Emitted]
  233. public static Proc InstantiateLambda(RubyScope/*!*/ scope, object self, BlockDispatcher/*!*/ dispatcher) {
  234. return (dispatcher.Method != null) ? new Proc(ProcKind.Lambda, self, scope, dispatcher) : null;
  235. }
  236. [Emitted]
  237. public static Proc/*!*/ DefineBlock(RubyScope/*!*/ scope, object self, BlockDispatcher/*!*/ dispatcher, object/*!*/ clrMethod) {
  238. // DLR closures should not be used:
  239. Debug.Assert(!(((Delegate)clrMethod).Target is Closure) || ((Closure)((Delegate)clrMethod).Target).Locals == null);
  240. return new Proc(ProcKind.Block, self, scope, dispatcher.SetMethod(clrMethod));
  241. }
  242. [Emitted]
  243. public static Proc/*!*/ DefineLambda(RubyScope/*!*/ scope, object self, BlockDispatcher/*!*/ dispatcher, object/*!*/ clrMethod) {
  244. // DLR closures should not be used:
  245. Debug.Assert(!(((Delegate)clrMethod).Target is Closure) || ((Closure)((Delegate)clrMethod).Target).Locals == null);
  246. return new Proc(ProcKind.Lambda, self, scope, dispatcher.SetMethod(clrMethod));
  247. }
  248. /// <summary>
  249. /// Used in a method call with a block to reset proc-kind when the call is retried
  250. /// </summary>
  251. [Emitted]
  252. public static void InitializeBlock(Proc/*!*/ proc) {
  253. Assert.NotNull(proc);
  254. proc.Kind = ProcKind.Block;
  255. }
  256. /// <summary>
  257. /// Implements END block - like if it was a call to at_exit { ... } library method.
  258. /// </summary>
  259. [Emitted]
  260. public static void RegisterShutdownHandler(Proc/*!*/ proc) {
  261. proc.LocalScope.RubyContext.RegisterShutdownHandler(proc);
  262. }
  263. #endregion
  264. #region Yield: TODO: generate
  265. [Emitted]
  266. public static object Yield0(Proc procArg, object self, BlockParam/*!*/ blockParam) {
  267. object result;
  268. var proc = blockParam.Proc;
  269. try {
  270. result = proc.Dispatcher.Invoke(blockParam, self, procArg);
  271. } catch(EvalUnwinder evalUnwinder) {
  272. result = blockParam.GetUnwinderResult(evalUnwinder);
  273. }
  274. return result;
  275. }
  276. [Emitted]
  277. public static object Yield1(object arg1, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  278. object result;
  279. var proc = blockParam.Proc;
  280. try {
  281. result = proc.Dispatcher.Invoke(blockParam, self, procArg, arg1);
  282. } catch (EvalUnwinder evalUnwinder) {
  283. result = blockParam.GetUnwinderResult(evalUnwinder);
  284. }
  285. return result;
  286. }
  287. // YieldNoAutoSplat1 uses InvokeNoAutoSplat instead of Invoke (used by Call1)
  288. internal static object YieldNoAutoSplat1(object arg1, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  289. object result;
  290. var proc = blockParam.Proc;
  291. try {
  292. result = proc.Dispatcher.InvokeNoAutoSplat(blockParam, self, procArg, arg1);
  293. } catch (EvalUnwinder evalUnwinder) {
  294. result = blockParam.GetUnwinderResult(evalUnwinder);
  295. }
  296. return result;
  297. }
  298. [Emitted]
  299. public static object Yield2(object arg1, object arg2, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  300. object result;
  301. var proc = blockParam.Proc;
  302. try {
  303. result = proc.Dispatcher.Invoke(blockParam, self, procArg, arg1, arg2);
  304. } catch (EvalUnwinder evalUnwinder) {
  305. result = blockParam.GetUnwinderResult(evalUnwinder);
  306. }
  307. return result;
  308. }
  309. [Emitted]
  310. public static object Yield3(object arg1, object arg2, object arg3, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  311. object result;
  312. var proc = blockParam.Proc;
  313. try {
  314. result = proc.Dispatcher.Invoke(blockParam, self, procArg, arg1, arg2, arg3);
  315. } catch (EvalUnwinder evalUnwinder) {
  316. result = blockParam.GetUnwinderResult(evalUnwinder);
  317. }
  318. return result;
  319. }
  320. [Emitted]
  321. public static object Yield4(object arg1, object arg2, object arg3, object arg4, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  322. object result;
  323. var proc = blockParam.Proc;
  324. try {
  325. result = proc.Dispatcher.Invoke(blockParam, self, procArg, arg1, arg2, arg3, arg4);
  326. } catch (EvalUnwinder evalUnwinder) {
  327. result = blockParam.GetUnwinderResult(evalUnwinder);
  328. }
  329. return result;
  330. }
  331. [Emitted]
  332. public static object YieldN(object[]/*!*/ args, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  333. Debug.Assert(args.Length > BlockDispatcher.MaxBlockArity);
  334. object result;
  335. var proc = blockParam.Proc;
  336. try {
  337. result = proc.Dispatcher.Invoke(blockParam, self, procArg, args);
  338. } catch (EvalUnwinder evalUnwinder) {
  339. result = blockParam.GetUnwinderResult(evalUnwinder);
  340. }
  341. return result;
  342. }
  343. internal static object Yield(object[]/*!*/ args, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  344. switch (args.Length) {
  345. case 0: return RubyOps.Yield0(procArg, self, blockParam);
  346. case 1: return RubyOps.Yield1(args[0], procArg, self, blockParam);
  347. case 2: return RubyOps.Yield2(args[0], args[1], procArg, self, blockParam);
  348. case 3: return RubyOps.Yield3(args[0], args[1], args[2], procArg, self, blockParam);
  349. case 4: return RubyOps.Yield4(args[0], args[1], args[2], args[3], procArg, self, blockParam);
  350. default: return RubyOps.YieldN(args, procArg, self, blockParam);
  351. }
  352. }
  353. [Emitted]
  354. public static object YieldSplat0(IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  355. object result;
  356. var proc = blockParam.Proc;
  357. try {
  358. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, splattee);
  359. } catch (EvalUnwinder evalUnwinder) {
  360. result = blockParam.GetUnwinderResult(evalUnwinder);
  361. }
  362. return result;
  363. }
  364. [Emitted]
  365. public static object YieldSplat1(object arg1, IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  366. object result;
  367. var proc = blockParam.Proc;
  368. try {
  369. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, arg1, splattee);
  370. } catch (EvalUnwinder evalUnwinder) {
  371. result = blockParam.GetUnwinderResult(evalUnwinder);
  372. }
  373. return result;
  374. }
  375. [Emitted]
  376. public static object YieldSplat2(object arg1, object arg2, IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  377. object result;
  378. var proc = blockParam.Proc;
  379. try {
  380. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, arg1, arg2, splattee);
  381. } catch (EvalUnwinder evalUnwinder) {
  382. result = blockParam.GetUnwinderResult(evalUnwinder);
  383. }
  384. return result;
  385. }
  386. [Emitted]
  387. public static object YieldSplat3(object arg1, object arg2, object arg3, IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  388. object result;
  389. var proc = blockParam.Proc;
  390. try {
  391. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, arg1, arg2, arg3, splattee);
  392. } catch (EvalUnwinder evalUnwinder) {
  393. result = blockParam.GetUnwinderResult(evalUnwinder);
  394. }
  395. return result;
  396. }
  397. [Emitted]
  398. public static object YieldSplat4(object arg1, object arg2, object arg3, object arg4, IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  399. object result;
  400. var proc = blockParam.Proc;
  401. try {
  402. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, arg1, arg2, arg3, arg4, splattee);
  403. } catch (EvalUnwinder evalUnwinder) {
  404. result = blockParam.GetUnwinderResult(evalUnwinder);
  405. }
  406. return result;
  407. }
  408. [Emitted]
  409. public static object YieldSplatN(object[]/*!*/ args, IList/*!*/ splattee, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  410. object result;
  411. var proc = blockParam.Proc;
  412. try {
  413. result = proc.Dispatcher.InvokeSplat(blockParam, self, procArg, args, splattee);
  414. } catch (EvalUnwinder evalUnwinder) {
  415. result = blockParam.GetUnwinderResult(evalUnwinder);
  416. }
  417. return result;
  418. }
  419. [Emitted]
  420. public static object YieldSplatNRhs(object[]/*!*/ args, IList/*!*/ splattee, object rhs, Proc procArg, object self, BlockParam/*!*/ blockParam) {
  421. object result;
  422. var proc = blockParam.Proc;
  423. try {
  424. result = proc.Dispatcher.InvokeSplatRhs(blockParam, self, procArg, args, splattee, rhs);
  425. } catch (EvalUnwinder evalUnwinder) {
  426. result = blockParam.GetUnwinderResult(evalUnwinder);
  427. }
  428. return result;
  429. }
  430. #endregion
  431. #region Methods
  432. [Emitted] // MethodDeclaration:
  433. public static object DefineMethod(object target, RubyScope/*!*/ scope, RubyMethodBody/*!*/ body) {
  434. Assert.NotNull(body, scope);
  435. RubyModule instanceOwner, singletonOwner;
  436. RubyMemberFlags instanceFlags, singletonFlags;
  437. bool moduleFunction = false;
  438. if (body.HasTarget) {
  439. if (!RubyUtils.CanDefineSingletonMethod(target)) {
  440. throw RubyExceptions.CreateTypeError("can't define singleton method for literals");
  441. }
  442. instanceOwner = null;
  443. instanceFlags = RubyMemberFlags.Invalid;
  444. singletonOwner = scope.RubyContext.GetOrCreateSingletonClass(target);
  445. singletonFlags = RubyMemberFlags.Public;
  446. } else {
  447. var attributesScope = scope.GetMethodAttributesDefinitionScope();
  448. if ((attributesScope.MethodAttributes & RubyMethodAttributes.ModuleFunction) == RubyMethodAttributes.ModuleFunction) {
  449. // Singleton module-function's scope points to the instance method's RubyMemberInfo.
  450. // This affects:
  451. // 1) super call
  452. // Super call is looking for Method.DeclaringModule while searching MRO, which would fail if the singleton module-function
  453. // was in MRO. Since module-function can only be used on module the singleton method could only be on module's singleton.
  454. // Module's singleton is never part of MRO so we are safe.
  455. // 2) trace
  456. // Method call trace reports non-singleton module.
  457. // MRI 1.8: instance method owner is self -> it is possible (via define_method) to define m.f. on a class (bug)
  458. // MRI 1.9: instance method owner GetMethodDefinitionOwner
  459. // MRI allows to define m.f. on classes but then doesn't work correctly with it.
  460. instanceOwner = scope.GetMethodDefinitionOwner();
  461. if (instanceOwner.IsClass) {
  462. throw RubyExceptions.CreateTypeError("A module function cannot be defined on a class.");
  463. }
  464. instanceFlags = RubyMemberFlags.Private;
  465. singletonOwner = instanceOwner.GetOrCreateSingletonClass();
  466. singletonFlags = RubyMemberFlags.Public;
  467. moduleFunction = true;
  468. } else {
  469. instanceOwner = scope.GetMethodDefinitionOwner();
  470. instanceFlags = (RubyMemberFlags)RubyUtils.GetSpecialMethodVisibility(attributesScope.Visibility, body.Name);
  471. singletonOwner = null;
  472. singletonFlags = RubyMemberFlags.Invalid;
  473. }
  474. }
  475. RubyMethodInfo instanceMethod = null, singletonMethod = null;
  476. if (instanceOwner != null) {
  477. SetMethod(scope.RubyContext, instanceMethod =
  478. new RubyMethodInfo(body, scope, instanceOwner, instanceFlags)
  479. );
  480. }
  481. if (singletonOwner != null) {
  482. SetMethod(scope.RubyContext, singletonMethod =
  483. new RubyMethodInfo(body, scope, singletonOwner, singletonFlags)
  484. );
  485. }
  486. // the method's scope saves the result => singleton module-function uses instance-method
  487. var method = instanceMethod ?? singletonMethod;
  488. method.DeclaringModule.MethodAdded(body.Name);
  489. if (moduleFunction) {
  490. Debug.Assert(!method.DeclaringModule.IsClass);
  491. method.DeclaringModule.GetOrCreateSingletonClass().MethodAdded(body.Name);
  492. }
  493. return null;
  494. }
  495. private static void SetMethod(RubyContext/*!*/ callerContext, RubyMethodInfo/*!*/ method) {
  496. var owner = method.DeclaringModule;
  497. // Do not trigger the add-method event just yet, we need to assign the result into closure before executing any user code.
  498. // If the method being defined is "method_added" itself, we would call that method before the info gets assigned to the closure.
  499. owner.SetMethodNoEvent(callerContext, method.DefinitionName, method);
  500. // expose RubyMethod in the scope (the method is bound to the main singleton instance):
  501. if (owner.GlobalScope != null) {
  502. RubyOps.ScopeSetMember(
  503. owner.GlobalScope.Scope,
  504. method.DefinitionName,
  505. new RubyMethod(owner.GlobalScope.MainObject, method, method.DefinitionName)
  506. );
  507. }
  508. }
  509. [Emitted] // AliasStatement:
  510. public static void AliasMethod(RubyScope/*!*/ scope, string/*!*/ newName, string/*!*/ oldName) {
  511. scope.GetMethodDefinitionOwner().AddMethodAlias(newName, oldName);
  512. }
  513. [Emitted] // UndefineMethod:
  514. public static void UndefineMethod(RubyScope/*!*/ scope, string/*!*/ name) {
  515. RubyModule owner = scope.GetMethodDefinitionOwner();
  516. if (!owner.ResolveMethod(name, VisibilityContext.AllVisible).Found) {
  517. throw RubyExceptions.CreateUndefinedMethodError(owner, name);
  518. }
  519. owner.UndefineMethod(name);
  520. }
  521. #endregion
  522. #region Modules
  523. [Emitted]
  524. public static RubyModule/*!*/ DefineGlobalModule(RubyScope/*!*/ scope, string/*!*/ name) {
  525. return DefineModule(scope, scope.Top.TopModuleOrObject, name);
  526. }
  527. [Emitted]
  528. public static RubyModule/*!*/ DefineNestedModule(RubyScope/*!*/ scope, string/*!*/ name) {
  529. return DefineModule(scope, scope.GetInnerMostModuleForConstantLookup(), name);
  530. }
  531. [Emitted]
  532. public static RubyModule/*!*/ DefineModule(RubyScope/*!*/ scope, object target, string/*!*/ name) {
  533. return DefineModule(scope, RubyUtils.GetModuleFromObject(scope, target), name);
  534. }
  535. // thread-safe:
  536. private static RubyModule/*!*/ DefineModule(RubyScope/*!*/ scope, RubyModule/*!*/ owner, string/*!*/ name) {
  537. Assert.NotNull(scope, owner);
  538. ConstantStorage existing;
  539. if (owner.TryGetConstant(scope.GlobalScope, name, out existing)) {
  540. RubyModule module = existing.Value as RubyModule;
  541. if (module == null || module.IsClass) {
  542. throw RubyExceptions.CreateTypeError(String.Format("{0} is not a module", name));
  543. }
  544. return module;
  545. } else {
  546. // create class/module object:
  547. return owner.Context.DefineModule(owner, name);
  548. }
  549. }
  550. #endregion
  551. #region Classes
  552. [Emitted]
  553. public static RubyClass/*!*/ DefineSingletonClass(RubyScope/*!*/ scope, object obj) {
  554. if (!RubyUtils.HasSingletonClass(obj)) {
  555. throw RubyExceptions.CreateTypeError(String.Format("no virtual class for {0}", scope.RubyContext.GetClassOf(obj).Name));
  556. }
  557. return scope.RubyContext.GetOrCreateSingletonClass(obj);
  558. }
  559. [Emitted]
  560. public static RubyModule/*!*/ DefineGlobalClass(RubyScope/*!*/ scope, string/*!*/ name, object superClassObject) {
  561. return DefineClass(scope, scope.Top.TopModuleOrObject, name, superClassObject);
  562. }
  563. [Emitted]
  564. public static RubyModule/*!*/ DefineNestedClass(RubyScope/*!*/ scope, string/*!*/ name, object superClassObject) {
  565. return DefineClass(scope, scope.GetInnerMostModuleForConstantLookup(), name, superClassObject);
  566. }
  567. [Emitted]
  568. public static RubyModule/*!*/ DefineClass(RubyScope/*!*/ scope, object target, string/*!*/ name, object superClassObject) {
  569. return DefineClass(scope, RubyUtils.GetModuleFromObject(scope, target), name, superClassObject);
  570. }
  571. // thread-safe:
  572. private static RubyClass/*!*/ DefineClass(RubyScope/*!*/ scope, RubyModule/*!*/ owner, string/*!*/ name, object superClassObject) {
  573. Assert.NotNull(owner);
  574. RubyClass superClass = ToSuperClass(owner.Context, superClassObject);
  575. ConstantStorage existing;
  576. if (owner.IsObjectClass
  577. ? owner.TryResolveConstant(scope.GlobalScope, name, out existing)
  578. : owner.TryGetConstant(scope.GlobalScope, name, out existing)) {
  579. RubyClass cls = existing.Value as RubyClass;
  580. if (cls == null || !cls.IsClass) {
  581. throw RubyExceptions.CreateTypeError("{0} is not a class", name);
  582. }
  583. if (superClassObject != null && !ReferenceEquals(cls.SuperClass, superClass)) {
  584. throw RubyExceptions.CreateTypeError("superclass mismatch for class {0}", name);
  585. }
  586. return cls;
  587. } else {
  588. return owner.Context.DefineClass(owner, name, superClass, null);
  589. }
  590. }
  591. private static RubyClass/*!*/ ToSuperClass(RubyContext/*!*/ ec, object superClassObject) {
  592. if (superClassObject != null) {
  593. RubyClass superClass = superClassObject as RubyClass;
  594. if (superClass == null) {
  595. throw RubyExceptions.CreateTypeError("superclass must be a Class ({0} given)", ec.GetClassOf(superClassObject).Name);
  596. }
  597. if (superClass.IsSingletonClass) {
  598. throw RubyExceptions.CreateTypeError("can't make subclass of virtual class");
  599. }
  600. return superClass;
  601. } else {
  602. return ec.ObjectClass;
  603. }
  604. }
  605. #endregion
  606. #region Constants
  607. /// <summary>
  608. /// A
  609. /// ::A
  610. /// </summary>
  611. [Emitted]
  612. public static object GetUnqualifiedConstant(RubyScope/*!*/ scope, ConstantSiteCache/*!*/ cache, string/*!*/ name, bool isGlobal) {
  613. object result = null;
  614. RubyModule missingConstantOwner;
  615. var context = scope.RubyContext;
  616. using (context.ClassHierarchyLocker()) {
  617. // Thread safety:
  618. // Another thread could have already updated the value, so the site version might be the same as CAV.
  619. // We do the lookup anyways since it is no-op and this only happens rarely.
  620. //
  621. // An important invariant holds here: in any time after initialized for the first time the Value field contains a valid value.
  622. // Threads can read an older value (the previous version) but that is still correct since we don't guarantee immediate
  623. // propagation of the constant write to all readers.
  624. //
  625. // if (site.Version = CAV) {
  626. // <- another thread could increment CAV here - we may return old or new value (both are ok)
  627. // value = site.Value;
  628. // } else {
  629. // <- another thread could get here as well and update the site before we get to update it.
  630. // GetConstant(...)
  631. // }
  632. // Constants might be updated during constant resolution due to autoload.
  633. // Any such updates need to invalidate the cache hence we need to capture the version before resolving the constant.
  634. int newVersion = context.ConstantAccessVersion;
  635. ConstantStorage storage;
  636. if (!isGlobal) {
  637. missingConstantOwner = scope.TryResolveConstantNoLock(scope.GlobalScope, name, out storage);
  638. } else if (context.ObjectClass.TryResolveConstantNoLock(scope.GlobalScope, name, out storage)) {
  639. missingConstantOwner = null;
  640. } else {
  641. missingConstantOwner = context.ObjectClass;
  642. }
  643. object newCacheValue;
  644. if (missingConstantOwner == null) {
  645. if (storage.WeakValue != null) {
  646. result = storage.Value;
  647. newCacheValue = storage.WeakValue;
  648. } else {
  649. result = newCacheValue = storage.Value;
  650. }
  651. } else {
  652. newCacheValue = ConstantSiteCache.WeakMissingConstant;
  653. }
  654. cache.Update(newCacheValue, newVersion);
  655. }
  656. if (missingConstantOwner != null) {
  657. result = missingConstantOwner.ConstantMissing(name);
  658. }
  659. return result;
  660. }
  661. /// <summary>
  662. /// A1::..::AN
  663. /// ::A1::..::AN
  664. /// </summary>
  665. [Emitted]
  666. public static object GetQualifiedConstant(RubyScope/*!*/ scope, ConstantSiteCache/*!*/ cache, string/*!*/[]/*!*/ qualifiedName, bool isGlobal) {
  667. var globalScope = scope.GlobalScope;
  668. var context = globalScope.Context;
  669. using (context.ClassHierarchyLocker()) {
  670. int newVersion = context.ConstantAccessVersion;
  671. ConstantStorage storage;
  672. bool anyMissing;
  673. RubyModule topModule = isGlobal ? context.ObjectClass : null;
  674. object result = ResolveQualifiedConstant(scope, qualifiedName, topModule, true, out storage, out anyMissing);
  675. // cache result only if no constant was missing:
  676. if (!anyMissing) {
  677. Debug.Assert(result == storage.Value);
  678. cache.Update(storage.WeakValue ?? result, newVersion);
  679. }
  680. return result;
  681. }
  682. }
  683. /// <summary>
  684. /// {expr}::A1::..::AN
  685. /// </summary>
  686. [Emitted]
  687. public static object GetExpressionQualifiedConstant(object target, RubyScope/*!*/ scope, ExpressionQualifiedConstantSiteCache/*!*/ cache,
  688. string/*!*/[]/*!*/ qualifiedName) {
  689. RubyModule module = target as RubyModule;
  690. if (module == null) {
  691. throw RubyUtils.CreateNotModuleException(scope, target);
  692. }
  693. var condition = cache.Condition;
  694. RubyContext context = module.Context;
  695. // Note that the module can be bound to another runtime:
  696. if (module.Id == condition.ModuleId && context.ConstantAccessVersion == condition.Version) {
  697. object value = cache.Value;
  698. if (value.GetType() == typeof(WeakReference)) {
  699. return ((WeakReference)value).Target;
  700. } else {
  701. return value;
  702. }
  703. }
  704. using (context.ClassHierarchyLocker()) {
  705. int newVersion = context.ConstantAccessVersion;
  706. ConstantStorage storage;
  707. bool anyMissing;
  708. object result = ResolveQualifiedConstant(scope, qualifiedName, module, true, out storage, out anyMissing);
  709. // cache result only if no constant was missing:
  710. if (!anyMissing) {
  711. Debug.Assert(result == storage.Value);
  712. cache.Update(storage.WeakValue ?? result, newVersion, module);
  713. }
  714. return result;
  715. }
  716. }
  717. /// <summary>
  718. /// defined? A
  719. /// </summary>
  720. [Emitted]
  721. public static bool IsDefinedUnqualifiedConstant(RubyScope/*!*/ scope, IsDefinedConstantSiteCache/*!*/ cache, string/*!*/ name) {
  722. var context = scope.RubyContext;
  723. using (context.ClassHierarchyLocker()) {
  724. int newVersion = context.ConstantAccessVersion;
  725. ConstantStorage storage;
  726. bool exists = scope.TryResolveConstantNoLock(null, name, out storage) == null;
  727. cache.Update(exists, newVersion);
  728. return exists;
  729. }
  730. }
  731. /// <summary>
  732. /// defined? ::A
  733. /// </summary>
  734. [Emitted]
  735. public static bool IsDefinedGlobalConstant(RubyScope/*!*/ scope, IsDefinedConstantSiteCache/*!*/ cache, string/*!*/ name) {
  736. var context = scope.RubyContext;
  737. using (context.ClassHierarchyLocker()) {
  738. int newVersion = context.ConstantAccessVersion;
  739. ConstantStorage storage;
  740. bool exists = context.ObjectClass.TryResolveConstantNoLock(null, name, out storage);
  741. cache.Update(exists, newVersion);
  742. return exists;
  743. }
  744. }
  745. /// <summary>
  746. /// defined? A1::..::AN
  747. /// defined? ::A1::..::AN
  748. /// </summary>
  749. [Emitted]
  750. public static bool IsDefinedQualifiedConstant(RubyScope/*!*/ scope, IsDefinedConstantSiteCache/*!*/ cache,
  751. string/*!*/[]/*!*/ qualifiedName, bool isGlobal) {
  752. var context = scope.RubyContext;
  753. using (context.ClassHierarchyLocker()) {
  754. int newVersion = context.ConstantAccessVersion;
  755. ConstantStorage storage;
  756. bool anyMissing;
  757. RubyModule topModule = isGlobal ? context.ObjectClass : null;
  758. RubyModule owner;
  759. try {
  760. owner = ResolveQualifiedConstant(scope, qualifiedName, topModule, false, out storage, out anyMissing) as RubyModule;
  761. } catch {
  762. // autoload can raise an exception
  763. scope.RubyContext.SetCurrentException(null);
  764. return false;
  765. }
  766. // Note that the owner could be another runtime's module:
  767. bool exists = owner != null && owner.TryResolveConstant(context, null, qualifiedName[qualifiedName.Length - 1], out storage);
  768. // cache result only if no constant was missing:
  769. if (!anyMissing) {
  770. cache.Update(exists, newVersion);
  771. }
  772. return exists;
  773. }
  774. }
  775. /// <summary>
  776. /// defined? {expr}::A
  777. /// defined? {expr}::A1::..::AN
  778. /// </summary>
  779. [Emitted]
  780. public static bool IsDefinedExpressionQualifiedConstant(object target, RubyScope/*!*/ scope,
  781. ExpressionQualifiedIsDefinedConstantSiteCache/*!*/ cache, string/*!*/[]/*!*/ qualifiedName) {
  782. RubyModule module = target as RubyModule;
  783. if (module == null) {
  784. return false;
  785. }
  786. var condition = cache.Condition;
  787. RubyContext context = module.Context;
  788. // Note that the module can be bound to another runtime:
  789. if (module.Id == condition.ModuleId && context.ConstantAccessVersion == condition.Version) {
  790. return cache.Value;
  791. }
  792. using (context.ClassHierarchyLocker()) {
  793. int newVersion = context.ConstantAccessVersion;
  794. ConstantStorage storage;
  795. bool exists;
  796. if (qualifiedName.Length == 1) {
  797. // Note that the owner could be another runtime's module:
  798. exists = module.TryResolveConstant(context, null, qualifiedName[0], out storage);
  799. } else {
  800. bool anyMissing;
  801. RubyModule owner;
  802. try {
  803. owner = ResolveQualifiedConstant(scope, qualifiedName, module, false, out storage, out anyMissing) as RubyModule;
  804. } catch {
  805. // autoload can raise an exception:
  806. return false;
  807. }
  808. // Note that the owner could be another runtime's module:
  809. exists = owner != null && owner.TryResolveConstant(context, null, qualifiedName[qualifiedName.Length - 1], out storage);
  810. // cache result only if no constant was missing:
  811. if (anyMissing) {
  812. return exists;
  813. }
  814. }
  815. cache.Update(exists, newVersion, module);
  816. return exists;
  817. }
  818. }
  819. private static object ResolveQualifiedConstant(RubyScope/*!*/ scope, string/*!*/[]/*!*/ qualifiedName, RubyModule topModule, bool isGet,
  820. out ConstantStorage storage, out bool anyMissing) {
  821. Debug.Assert(qualifiedName.Length >= 2 || qualifiedName.Length == 1 && isGet);
  822. RubyContext context = scope.RubyContext;
  823. context.RequiresClassHierarchyLock();
  824. RubyModule missingConstantOwner;
  825. RubyGlobalScope globalScope = scope.GlobalScope;
  826. int nameCount = (isGet) ? qualifiedName.Length : qualifiedName.Length - 1;
  827. string name = qualifiedName[0];
  828. if (topModule == null) {
  829. missingConstantOwner = scope.TryResolveConstantNoLock(globalScope, name, out storage);
  830. } else if (topModule.TryResolveConstant(context, globalScope, name, out storage)) {
  831. missingConstantOwner = null;
  832. } else {
  833. missingConstantOwner = topModule;
  834. }
  835. object result;
  836. if (missingConstantOwner == null) {
  837. result = storage.Value;
  838. anyMissing = false;
  839. } else {
  840. anyMissing = true;
  841. using (context.ClassHierarchyUnlocker()) {
  842. result = missingConstantOwner.ConstantMissing(name);
  843. }
  844. }
  845. for (int i = 1; i < nameCount; i++) {
  846. RubyModule owner = RubyUtils.GetModuleFromObject(scope, result);
  847. // Note that the owner could be another runtime's module:
  848. name = qualifiedName[i];
  849. if (owner.TryResolveConstant(context, globalScope, name, out storage)) {
  850. // Constant write updates constant version in a single runtime only.
  851. // Therefore if the chain mixes modules from different runtimes we cannot cache the result.
  852. if (owner.Context != context) {
  853. anyMissing = true;
  854. }
  855. result = storage.Value;
  856. } else {
  857. anyMissing = true;
  858. using (context.ClassHierarchyUnlocker()) {
  859. result = owner.ConstantMissing(name);
  860. }
  861. }
  862. }
  863. return result;
  864. }
  865. [Emitted]
  866. public static object GetMissingConstant(RubyScope/*!*/ scope, ConstantSiteCache/*!*/ cache, string/*!*/ name) {
  867. return scope.GetInnerMostModuleForConstantLookup().ConstantMissing(name);
  868. }
  869. [Emitted]
  870. public static object GetGlobalMissingConstant(RubyScope/*!*/ scope, ConstantSiteCache/*!*/ cache, string/*!*/ name) {
  871. return scope.RubyContext.ObjectClass.ConstantMissing(name);
  872. }
  873. [Emitted] // ConstantVariable:
  874. public static object SetGlobalConstant(object value, RubyScope/*!*/ scope, string/*!*/ name) {
  875. RubyUtils.SetConstant(scope.RubyContext.ObjectClass, name, value);
  876. return value;
  877. }
  878. [Emitted] // ConstantVariable:
  879. public static object SetUnqualifiedConstant(object value, RubyScope/*!*/ scope, string/*!*/ name) {
  880. RubyUtils.SetConstant(scope.GetInnerMostModuleForConstantLookup(), name, value);
  881. return value;
  882. }
  883. [Emitted] // ConstantVariable:
  884. public static object SetQualifiedConstant(object value, object target, RubyScope/*!*/ scope, string/*!*/ name) {
  885. RubyUtils.SetConstant(RubyUtils.GetModuleFromObject(scope, target), name, value);
  886. return value;
  887. }
  888. #endregion
  889. // MakeArray*
  890. public const int OptimizedOpCallParamCount = 5;
  891. #region MakeArray
  892. [Emitted]
  893. public static RubyArray/*!*/ MakeArray0() {
  894. return new RubyArray(0);
  895. }
  896. [Emitted]
  897. public static RubyArray/*!*/ MakeArray1(object item1) {
  898. RubyArray result = new RubyArray(1);
  899. result.Add(item1);
  900. return result;
  901. }
  902. [Emitted]
  903. public static RubyArray/*!*/ MakeArray2(object item1, object item2) {
  904. RubyArray result = new RubyArray(2);
  905. result.Add(item1);
  906. result.Add(item2);
  907. return result;
  908. }
  909. [Emitted]
  910. public static RubyArray/*!*/ MakeArray3(object item1, object item2, object item3) {
  911. RubyArray result = new RubyArray(3);
  912. result.Add(item1);
  913. result.Add(item2);
  914. result.Add(item3);
  915. return result;
  916. }
  917. [Emitted]
  918. public static RubyArray/*!*/ MakeArray4(object item1, object item2, object item3, object item4) {
  919. RubyArray result = new RubyArray(4);
  920. result.Add(item1);
  921. result.Add(item2);
  922. result.Add(item3);
  923. result.Add(item4);
  924. return result;
  925. }
  926. [Emitted]
  927. public static RubyArray/*!*/ MakeArray5(object item1, object item2, object item3, object item4, object item5) {
  928. RubyArray result = new RubyArray(5);
  929. result.Add(item1);
  930. result.Add(item2);
  931. result.Add(item3);
  932. result.Add(item4);
  933. result.Add(item5);
  934. return result;
  935. }
  936. [Emitted]
  937. public static RubyArray/*!*/ MakeArrayN(object[]/*!*/ items) {
  938. Debug.Assert(items != null);
  939. var array = new RubyArray(items.Length);
  940. array.AddVector(items, 0, items.Length);
  941. return array;
  942. }
  943. #endregion
  944. #region MakeHash
  945. [Emitted]
  946. public static Hash/*!*/ MakeHash0(RubyScope/*!*/ scope) {
  947. return new Hash(scope.RubyContext.EqualityComparer, 0);
  948. }
  949. [Emitted]
  950. public static Hash/*!*/ MakeHash(RubyScope/*!*/ scope, object[]/*!*/ items) {
  951. return RubyUtils.SetHashElements(scope.RubyContext, new Hash(scope.RubyContext.EqualityComparer, items.Length / 2), items);
  952. }
  953. #endregion
  954. #region Array
  955. [Emitted]
  956. public static RubyArray/*!*/ AddRange(RubyArray/*!*/ array, IList/*!*/ list) {
  957. return array.AddRange(list);
  958. }
  959. [Emitted] // method call:
  960. public static RubyArray/*!*/ AddSubRange(RubyArray/*!*/ result, IList/*!*/ array, int start, int count) {
  961. return result.AddRange(array, start, count);
  962. }
  963. [Emitted]
  964. public static RubyArray/*!*/ AddItem(RubyArray/*!*/ array, object item) {
  965. array.Add(item);
  966. return array;
  967. }
  968. [Emitted]
  969. public static IList/*!*/ SplatAppend(IList/*!*/ array, IList/*!*/ list) {
  970. Utils.AddRange(array, list);
  971. return array;
  972. }
  973. [Emitted]
  974. public static object Splat(IList/*!*/ list) {
  975. if (list.Count <= 1) {
  976. return (list.Count > 0) ? list[0] : null;
  977. }
  978. return list;
  979. }
  980. // 1.8 behavior
  981. [Emitted]
  982. public static object SplatPair(object value, IList/*!*/ list) {
  983. if (list.Count == 0) {
  984. return value;
  985. }
  986. RubyArray result = new RubyArray(list.Count + 1);
  987. result.Add(value);
  988. result.AddRange(list);
  989. return result;
  990. }
  991. [Emitted]
  992. public static IList/*!*/ Unsplat(object splattee) {
  993. var list = splattee as IList;
  994. if (list == null) {
  995. list = new RubyArray(1);
  996. list.Add(splattee);
  997. }
  998. return list;
  999. }
  1000. // CaseExpression
  1001. [Emitted]
  1002. public static bool ExistsUnsplatCompare(CallSite<Func<CallSite, obje

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