edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C420856 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C420856 (server) 4/29/2008 10:14 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;Procs5 @@ -93,7 +93,9 @@ Scenario_ParallelAssignment10, Scenario_RubyBlocks0, - Scenario_RubyBlocks1, + ProcYieldCaching1, + ProcCallCaching1, + ProcSelf1, Scenario_RubyBlocks2, Scenario_RubyBlocks3, Scenario_RubyBlocks5, @@ -261,6 +263,12 @@ LocalNames1, Binding1, TopLevelBinding_RubyProgram, + EvalWithProcBinding1, + ModuleEvalProc1, + ModuleEvalProc2, + ModuleEvalProc3, + ModuleEvalString1, + ModuleEvalString2, }; } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/BlockTests.cs;C417101 File: BlockTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/BlockTests.cs;C417101 (server) 4/29/2008 10:53 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/BlockTests.cs;Procs5 @@ -39,7 +39,7 @@ }, "012"); } - public void Scenario_RubyBlocks1() { + public void ProcYieldCaching1() { AssertOutput(delegate() { CompilerTest(@" def foo @@ -53,6 +53,31 @@ }, "ABC"); } + public void ProcCallCaching1() { + AssertOutput(delegate() { + CompilerTest(@" +$p = lambda { puts 1 } +$q = lambda { puts 2 } + +$p.call +$q.call +"); + }, @" +1 +2 +"); + } + + public void ProcSelf1() { + AssertOutput(delegate() { + CompilerTest(@" +module M + 1.times { p self } +end +"); + }, @"M"); + } + public void Scenario_RubyBlocks2() { AssertExceptionThrown(delegate() { CompilerTest(@" =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;C420856 File: EvalTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;C420856 (server) 4/29/2008 11:06 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;Procs5 @@ -117,5 +117,115 @@ 7 nil"); } + + public void EvalWithProcBinding1() { + AssertOutput(delegate() { + CompilerTest(@" +def goo + x = 1 + y = 2 + lambda { + z = -1 + } +end + +def z + 1 +end + +p eval('x+y+z', goo) +"); + }, @"4"); + } + + /// + /// "self" and arguments are passed in the proc, result is passed out. + /// + public void ModuleEvalProc1() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +puts M.module_eval { |*a| + p a, self + 'result' +} +"); + }, @" +[M] +M +result +"); + } + + /// + /// Break from module_eval'd proc works. + /// + public void ModuleEvalProc2() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +puts M.module_eval { + break 'result' +} +"); + }, @" +result +"); + } + + /// + /// module_eval uses yield semantics for invoking the proc. + /// (return in a yield to inactive lambda doesn't work). + /// + public void ModuleEvalProc3() { + AssertOutput(delegate() { + CompilerTest(@" +module M +end + +$p = lambda { + return 'ok' +} + +puts $p.call +puts M.module_eval(&$p) rescue puts 'error' +"); + }, @" +ok +error +"); + } + + public void ModuleEvalString1() { + AssertOutput(delegate() { + CompilerTest(@" +module N + module M + end +end + +N::M.module_eval('p self, Module.nesting') +"); + }, @" +N::M +[N::M] +"); + } + + public void ModuleEvalString2() { + AssertOutput(delegate() { + CompilerTest(@" +class C +end + +C.module_eval('def foo; puts 1; end') +C.new.foo +"); + }, @"1"); + } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/HashTests.cs;C417101 File: HashTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/HashTests.cs;C417101 (server) 4/29/2008 10:13 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/HashTests.cs;Procs5 @@ -98,6 +98,5 @@ "); }, @"abc"); } - } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;C422137 File: Initializer.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;C422137 (server) 4/29/2008 5:04 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializer.Generated.cs;Procs5 @@ -43,7 +43,7 @@ #endif // Skipped primitive: Object Ruby.Builtins.RubyModule def27 = DefineModule("System::Collections::Generic::IDictionary", typeof(System.Collections.Generic.IDictionary), new System.Action(LoadSystem__Collections__Generic__IDictionary_Instance), null, new Ruby.Builtins.RubyModule[] {def21, }); - Ruby.Builtins.RubyModule def39 = DefineModule("System::Collections::IEnumerable", typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new Ruby.Builtins.RubyModule[] {def21, }); + DefineModule("System::Collections::IEnumerable", typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new Ruby.Builtins.RubyModule[] {def21, }); Ruby.Builtins.RubyModule def35 = DefineModule("System::Collections::IList", typeof(System.Collections.IList), new System.Action(LoadSystem__Collections__IList_Instance), null, new Ruby.Builtins.RubyModule[] {def21, }); DefineModule("System::IComparable", typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new Ruby.Builtins.RubyModule[] {def33, }); DefineGlobalClass("Time", typeof(System.DateTime), typeof(Ruby.Builtins.TimeOps), new System.Action(LoadTime_Instance), new System.Action(LoadTime_Class), classRef1, new Ruby.Builtins.RubyModule[] {def33, }, new System.Delegate[] { @@ -70,7 +70,7 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ArrayOps.CreateArray), }); DefineGlobalClass("Binding", typeof(Ruby.Builtins.Binding), typeof(Ruby.Builtins.BindingOps), null, null, Context.ObjectClass, Ruby.Builtins.RubyModule.EmptyArray, null); - DefineGlobalClass("ClrString", typeof(System.String), typeof(Ruby.Builtins.StringOps), new System.Action(LoadClrString_Instance), null, Context.ObjectClass, new Ruby.Builtins.RubyModule[] {def39, }, null); + DefineGlobalClass("ClrString", typeof(System.String), typeof(Ruby.Builtins.StringOps), new System.Action(LoadClrString_Instance), null, Context.ObjectClass, Ruby.Builtins.RubyModule.EmptyArray, null); Ruby.Builtins.RubyClass def13 = DefineClass("Digest::Class", typeof(Ruby.StandardLibrary.Digest.Class), null, null, new System.Action(LoadDigest__Class_Class), Context.ObjectClass, new Ruby.Builtins.RubyModule[] {def14, }, null); DefineGlobalClass("Dir", typeof(Ruby.Builtins.RubyDir), null, new System.Action(LoadDir_Instance), new System.Action(LoadDir_Class), Context.ObjectClass, new Ruby.Builtins.RubyModule[] {def21, }, null); Ruby.Builtins.RubyClass def34 = Context.ExceptionClass = DefineGlobalClass("Exception", typeof(System.Exception), typeof(Ruby.Builtins.ExceptionOps), new System.Action(LoadException_Instance), new System.Action(LoadException_Class), Context.ObjectClass, Ruby.Builtins.RubyModule.EmptyArray, new System.Delegate[] { @@ -1762,8 +1762,8 @@ }); module.DefineMethod("eval", 0xa, new System.Delegate[] { - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), }); module.DefineMethod("exit", 0xa, new System.Delegate[] { @@ -1972,8 +1972,8 @@ }); module.DefineMethod("eval", 0x11, new System.Delegate[] { - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), }); module.DefineMethod("exit", 0x11, new System.Delegate[] { @@ -2302,9 +2302,6 @@ }); module.DefineMethod("class_eval", 0x9, new System.Delegate[] { - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), - new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), }); @@ -2386,7 +2383,8 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.MethodDefined), }); - module.DefineMethod("module_eval", 0x9, new System.Delegate[] { + module.DefineMethod("module_eval", 0xa, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), }); @@ -2475,6 +2473,10 @@ private void LoadModule_Class(Ruby.Builtins.RubyModule/*!*/ module) { + module.DefineMethod("class_eval", 0x11, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.Evaluate), + }); + module.DefineMethod("constants", 0x11, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.ModuleOps.GetGlobalConstants), }); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C420856 File: Kernel.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C420856 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;Procs5 @@ -124,30 +124,19 @@ [RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] [RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] - public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, [Optional]Binding binding, [Optional, NotNull]MutableString file) { - return Evaluate(context, self, code, (binding != null) ? binding.LocalScope : RubyUtils.GetScope(context), file, null); + public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, + [Optional]Binding binding, [Optional, NotNull]MutableString file, [Optional, DefaultValue(1)]int line) { + return Evaluate(context, self, code, (binding != null) ? binding.LocalScope : RubyUtils.GetScope(context), file, line); } [RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] [RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] - public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, [Optional]Binding binding, [Optional, NotNull]MutableString file, int line) { - return Evaluate(context, self, code, (binding != null) ? binding.LocalScope : RubyUtils.GetScope(context), file, line); + public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, + [NotNull]Proc/*!*/ procBinding, [Optional, NotNull]MutableString file, [Optional, DefaultValue(1)]int line) { + return Evaluate(context, self, code, procBinding.LocalScope, file, line); } - // TODO: - //[RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] - //[RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] - //public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, [NotNull]Proc/*!*/ procBinding, [Optional, NotNull]MutableString file) { - // return Evaluate(context, self, code, procBinding.LocalScope, file, null); - //} - - //[RubyMethod("eval", RubyMethodAttributes.PrivateInstance)] - //[RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] - //public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, [NotNull]Proc/*!*/ procBinding, [Optional, NotNull]MutableString file, int line) { - // return Evaluate(context, self, code, procBinding.LocalScope, file, line); - //} - - internal static object Evaluate(CodeContext/*!*/ context, object self, MutableString/*!*/ code, RubyScope/*!*/ targetScope, MutableString file, int? line) { + internal static object Evaluate(CodeContext/*!*/ context, object self, MutableString/*!*/ code, RubyScope/*!*/ targetScope, MutableString file, int line) { Assert.NotNull(context, code, targetScope); RubyContext language = (RubyContext)context.LanguageContext; @@ -591,6 +580,7 @@ } #endregion +#if TODO static DynamicSite/*!*/ _InstanceEvalSite = CallSiteFactory.CreateSimpleCallSite(RubyContext.RubyBinder); [RubyMethod("instance_eval")] @@ -598,7 +588,6 @@ if (block == null) { throw RubyExceptions.CreateArgumentError("block not supplied"); } - // TODO: for modules/classes, this has different semantics. For example: // class Test; end // Test.instance_eval { def test_instance_eval; end } @@ -611,8 +600,32 @@ BlockParam newBlock = block.CloneWithNewSelf(self); return _InstanceEvalSite.Invoke(context, newBlock); + throw new NotImplementedException("TODO"); } +#endif + private static DynamicSite _InstanceEvalSite = + DynamicSite.Create(YieldAction.Make(RubyContext.RubyBinder, 2)); + + // TODO: string + // TODO: method defs -> singletons, self == module? + // probably use module_function + [RubyMethod("instance_eval")] + public static object InstanceEval(CodeContext/*!*/ context, object self, BlockParam/*!*/ block) { + if (block == null) { + throw RubyExceptions.CreateArgumentError("block not supplied"); + } + + object returnValue = _InstanceEvalSite.Invoke(context, + block, // block param + self, // self object + self // argument + ); + + block.BlockJumped(returnValue); + return returnValue; + } + #region instance_of?, is_a?, kind_of? [RubyMethod("is_a?")] =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C422137 File: ModuleOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C422137 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;Procs5 @@ -23,6 +23,9 @@ using Ruby.Runtime.Calls; using Microsoft.Scripting.Runtime; using Ruby.Compiler; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Diagnostics; namespace Ruby.Builtins { @@ -432,48 +435,50 @@ #endregion - #region class_eval - [RubyMethod("class_eval")] - public static object Evaluate(CodeContext/*!*/ context, RubyModule/*!*/ module, MutableString code) { - throw new NotImplementedException(); -#if FALSE - // Create a new context for running the code string - not sure if this is necessary. - CodeContext context2 = new CodeContext(context.Scope, context.LanguageContext, context); - // We need a localscope for GetRfc. - RubyScope localScope = RubyUtils.GetScope(context); - // Create a local scope for this module definition. - // (This has a side-effect that context2 has the new module scope attached in its LocalScope property.) - RubyScope moduleScope = RubyOps.CreateModuleScope(context2, localScope.RuntimeFlowControl, module); - // Compile up the code in the new context - SourceUnit sourceUnit = context2.LanguageContext.CreateSnippet(code, SourceCodeKind.File); - ScriptCode scriptCode = sourceUnit.Compile(context2.LanguageContext.GetCompilerOptions(context2.Scope), ErrorSink.Default); - // Run the code in the new context. - // TODO: (This overload is currently private but we need to be able to pass in the new context). - // return scriptCode.Run(context2, false); - throw RubyExceptions.CreateNotImplementedError("We need ScriptCode.Run(CodeContext, bool) to be public"); -#endif - } + #region module_eval, class_eval - [RubyMethod("class_eval")] - public static object Evaluate(object self, string code, string fileName) { - return null; - } + [RubyMethod("module_eval", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("class_eval", RubyMethodAttributes.PublicSingleton)] + public static object Evaluate(CodeContext/*!*/ context, RubyModule/*!*/ self, [NotNull]MutableString/*!*/ code, + [Optional, NotNull]MutableString file, [Optional, DefaultValue(1)]int line) { - [RubyMethod("class_eval")] - public static object Evaluate(object self, string code, string fileName, int lineNumber) { - return null; + Assert.NotNull(context, self, code); + + RubyContext language = (RubyContext)context.LanguageContext; + SourceUnit source = language.CreateSnippet(code.ToString()); + + RubyCompilerOptions options = new RubyCompilerOptions(); + + // we want to create a new top-level local scope: + options.IsIncluded = true; + + ScriptCode compiledCode = source.Compile(options, language.ExecutionContext.RuntimeErrorSink); + Debug.Assert(compiledCode != null); + + // TODO: this is hack - we need arbitrary signature of the Initialize method: + ModuleEvalScope moduleEvalScope = new ModuleEvalScope((RubyScope)context, self); + + return compiledCode.Run(moduleEvalScope, false); } - static DynamicSite/*!*/ _ClassEvalSite = CallSiteFactory.CreateSimpleCallSite(RubyContext.RubyBinder); + private static DynamicSite _ClassEvalSite = + DynamicSite.Create(YieldAction.Make(RubyContext.RubyBinder, 2)); [RubyMethod("class_eval")] [RubyMethod("module_eval")] - public static object Evaluate(CodeContext/*!*/ context, RubyModule/*!*/ self, BlockParam block) { + public static object Evaluate(CodeContext/*!*/ context, RubyModule/*!*/ self, BlockParam/*!*/ block) { if (block == null) { throw RubyExceptions.CreateArgumentError("block not supplied"); } - BlockParam newBlock = block.CloneWithNewSelf(self); - return _ClassEvalSite.Invoke(context, newBlock); + + object returnValue = _ClassEvalSite.Invoke(context, + block, // block param + self, // self object + self // argument + ); + + block.BlockJumped(returnValue); + return returnValue; } #endregion =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;C420856 File: Ruby.csproj =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;C420856 (server) 4/29/2008 4:26 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Ruby.csproj;Procs5 @@ -113,6 +113,7 @@ + =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/Proc.cs;C402444 File: Proc.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/Proc.cs;C402444 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/Proc.cs;Procs5 @@ -13,19 +13,23 @@ * * ***************************************************************************/ +using System; using System.Reflection; using System.Threading; +using System.Diagnostics; + using Microsoft.Scripting; using Microsoft.Scripting.Actions; using Microsoft.Scripting.Ast; using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; + using Ruby.Runtime; using Ruby.Runtime.Calls; -using Microsoft.Scripting.Runtime; namespace Ruby.Builtins { using Ast = Microsoft.Scripting.Ast.Expression; - + internal enum ProcKind { Block, Proc, @@ -38,9 +42,6 @@ private readonly RubyBlockInfo/*!*/ _info; private ProcKind _kind; - // blocks are closed over self and context: - private object/*!*/ _self; // TODO: it shouldn't be here, should be passed as arg to block method - private readonly CodeContext/*!*/ _context; private readonly int _id; // we need to remember the block's owner and proc-converter frames: @@ -58,94 +59,141 @@ internal RubyBlockInfo Info { get { return _info; } } - // TODO (hack): Added setter to enable define_method. The block should take self parameter instead! - public object/*!*/ Self { get { return _self; } internal set { _self = value; } } - public int Id { get { return _id; } } + public object Self { + get { return _info.Self; } + } + + public RubyScope LocalScope { + get { return _info.LocalScope; } + } + internal static PropertyInfo/*!*/ IdProperty { get { return typeof(Proc).GetProperty("Id"); } } internal static PropertyInfo/*!*/ SelfProperty { get { return typeof(Proc).GetProperty("Self"); } } - internal Proc(ProcKind kind, RubyBlockInfo/*!*/ info, object/*!*/ self) { - Assert.NotNull(info, self); + internal Proc(ProcKind kind, RubyBlockInfo/*!*/ info) { + Assert.NotNull(info); _kind = kind; _info = info; - _context = info.Context; - _self = self; _id = Interlocked.Increment(ref _CurrentId); } #region IDynamicObject Members public LanguageContext/*!*/ LanguageContext { - get { return _context.LanguageContext; } + get { return RubyContext._Default; } } public RuleBuilder/*!*/ GetRule(DynamicAction/*!*/ action, CodeContext/*!*/ context, object[]/*!*/ args) { switch (action.Kind) { case DynamicActionKind.Call: - RuleBuilder result = new RuleBuilder(); - ActionBinder binder = context.LanguageContext.Binder; - SetCallRule(result, binder, context, new CallArguments(args, result.Parameters, (CallAction)action), result.Parameters[0]); - return result; + RuleBuilder rule = new RuleBuilder(); + + SetCallActionRule(rule, context.LanguageContext.Binder, context, + new CallArguments(args, rule.Parameters, (CallAction)action)); + + return rule; default: return null; } } + internal void SetCallActionRule(RuleBuilder/*!*/ rule, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, + CallArguments/*!*/ callActionArgs) { + + Assert.NotNull(rule, binder, callActionArgs); + Debug.Assert(callActionArgs.Length > 0 && typeof(Proc).IsAssignableFrom(callActionArgs.Values[0].GetType()), + "Call signature should be (Proc, user-args)"); + + Expression procExpression = Ast.Convert(callActionArgs.Expressions[0], typeof(Proc)); + + // removes proc: + CallArguments yieldArgs = callActionArgs.RemoveAt(0); + + // test for proc identity: + rule.AddTest(Ast.Equal(Ast.ReadProperty(procExpression, IdProperty), Ast.Constant(_id))); + + SetProcCallRule(rule, binder, callerContext, + procExpression, // proc object + Ast.ReadProperty(procExpression, Proc.SelfProperty), // self captured by the block closure + yieldArgs // user args + ); + } + /// - /// Yield. + /// From control flow perspective it "calls" the proc. /// - internal void SetCallRule(RuleBuilder/*!*/ result, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, - CallArguments/*!*/ args, Expression/*!*/ procExpression) { + internal void SetProcCallRule(RuleBuilder/*!*/ rule, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, + Expression/*!*/ procExpression, // proc object + Expression/*!*/ selfExpression, // self passed to the proc + CallArguments/*!*/ yieldArgs // user arguments passed to the proc + ) { - Assert.NotNull(result, binder, args); - Assert.NotNull(procExpression); + Expression bfcVariable = Ast.Temporary(typeof(BlockParam), "#bfc"); - result.AddTest( - Ast.Equal( - Ast.ReadProperty( - Ast.Convert(procExpression, IdProperty.DeclaringType), - IdProperty - ), - Ast.Constant(_id) - ) + _info.SetYieldRule(binder, callerContext, rule, + bfcVariable, // block param + selfExpression, // self + yieldArgs, // user args + + // transformation on the action in the rule target ensures the control flow "proc-call" semantics: + delegate(Expression/*!*/ expression) { + Expression resultVariable = Ast.Temporary(typeof(object), "#result"); + Expression evalUnwinder = Ast.Temporary(typeof(EvalUnwinder), "#unwinder"); + + return Ast.Comma( + Ast.Assign(bfcVariable, + Ast.Call(BlockParam.GetMethod("CreateForProcCall"), Ast.ConvertHelper(procExpression, typeof(Proc))) + ), + Ast.Try( + Ast.Write(resultVariable, expression) + ).Catch(typeof(EvalUnwinder), evalUnwinder, + Ast.Write(resultVariable, Ast.Call(bfcVariable, BlockParam.GetMethod("GetUnwinderResult"), evalUnwinder)) + ), + Ast.Call(RuntimeFlowControl.GetMethod("MethodProcCall"), bfcVariable, resultVariable), + resultVariable + ); + } ); - - // take the context the block is defined with: - _info.SetInvocationRule(null, binder, callerContext, result, args); } #endregion #region Block helper methods - // TODO: + // TODO: remove context, rename to CreateBlock public static Proc Create(CodeContext/*!*/ context, Function/*!*/ clrMethod) { - return RubyOps.DefineBlock(context, null, clrMethod, (Function)BlockCallback1, 1, false, false); + return Create((Function)BlockCallback1, clrMethod, 1); } - public static Proc Create(CodeContext/*!*/ context, Function/*!*/ clrMethod) { - return RubyOps.DefineBlock(context, null, clrMethod, (Function)BlockCallback2, 2, false, false); + public static Proc/*!*/ Create(CodeContext/*!*/ context, Function/*!*/ clrMethod) { + return Create((Function)BlockCallback2, clrMethod, 2); } - public static Proc Create(CodeContext/*!*/ context, Function/*!*/ clrMethod) { - return RubyOps.DefineBlock(context, null, clrMethod, (Function)BlockCallback3, 3, false, false); + public static Proc/*!*/ Create(CodeContext/*!*/ context, Function/*!*/ clrMethod) { + return Create((Function)BlockCallback3, clrMethod, 3); } - public static object BlockCallback1(BlockParam/*!*/ block, object arg0) { - Function clrMethod = (Function)block.Proc.Self; + public static Proc/*!*/ Create(Delegate/*!*/ clrMethod, object self, int parameterCount) { + return new Proc(ProcKind.Block, new RubyBlockInfo(clrMethod, self, null, parameterCount, false, false)); + } + + // The following methods are block implementations, there fore they need to have signature like a block. + // We need to get to the real delegate to call. We capture it into the self object. + public static object BlockCallback1(BlockParam/*!*/ block, object self, object arg0) { + Function clrMethod = (Function)self; return clrMethod(block, arg0); } - public static object BlockCallback2(BlockParam/*!*/ block, object arg0, object arg1) { - Function clrMethod = (Function)block.Proc.Self; + public static object BlockCallback2(BlockParam/*!*/ block, object self, object arg0, object arg1) { + Function clrMethod = (Function)self; return clrMethod(block, arg0, arg1); } - public static object BlockCallback3(BlockParam/*!*/ block, object arg0, object arg1, object arg2) { - Function clrMethod = (Function)block.Proc.Self; + public static object BlockCallback3(BlockParam/*!*/ block, object self, object arg0, object arg1, object arg2) { + Function clrMethod = (Function)self; return clrMethod(block, arg0, arg1, arg2); } @@ -156,7 +204,7 @@ /// /// public Proc/*!*/ ToLambda() { - Proc result = new Proc(ProcKind.Lambda, _info, _self); + Proc result = new Proc(ProcKind.Lambda, _info); result._owner = _owner; result._converter = _converter; return result; =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/BlockDefinition.cs;C420856 File: BlockDefinition.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/BlockDefinition.cs;C420856 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/BlockDefinition.cs;Procs5 @@ -31,8 +31,8 @@ public partial class BlockDefinition : Block { // { |args| body } - // block flow control: - internal const int HiddenParameterCount = 1; + // self, block flow control: + internal const int HiddenParameterCount = 2; private readonly CompoundLeftValue/*!*/ _parameters; private readonly List/*!*/ _body; @@ -58,7 +58,10 @@ _parameters = parameters; } - private MSA.ParameterExpression[]/*!*/ DefineParameters(MSA.LambdaBuilder/*!*/ block) { + private MSA.ParameterExpression[]/*!*/ DefineParameters(MSA.LambdaBuilder/*!*/ block, + out MSA.ParameterExpression/*!*/ selfVariable, + out MSA.ParameterExpression/*!*/ blockParamVariable) { + MSA.ParameterExpression[] parameters = new MSA.ParameterExpression[ HiddenParameterCount + _parameters.LeftValues.Count + @@ -66,7 +69,9 @@ ]; // hidden parameters: - parameters[0] = Ast.Parameter(typeof(BlockParam), "#proc"); + // #proc must be the first one - it is used as instance target for method invocation: + blockParamVariable = parameters[0] = Ast.Parameter(typeof(BlockParam), "#proc"); + selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self-param"); // TODO for (int i = HiddenParameterCount; i < parameters.Length; i++) { parameters[i] = Ast.Parameter(typeof(object), "#" + (i - HiddenParameterCount)); @@ -86,16 +91,18 @@ string id = System.Threading.Interlocked.Increment(ref _id).ToString(); // define hidden parameters and RHS-placeholders (#1..#n will be used as RHS of a parallel assignment): - MSA.ParameterExpression[] parameters = DefineParameters(codeBlock); - MSA.VariableExpression selfVariable = codeBlock.CreateLocalVariable(SymbolTable.StringToId("#self" + id), typeof(object)); - // TODO: + MSA.ParameterExpression blockParamVariable, selfParameter; + // TODO: remove + MSA.VariableExpression selfVariable = codeBlock.CreateTemporaryVariable(SymbolTable.StringToId("#self-temp"), typeof(object)); + MSA.ParameterExpression[] parameters = DefineParameters(codeBlock, out selfParameter, out blockParamVariable); + // TODO: MSA.VariableExpression scopeVariable = gen.CurrentScopeVariable; //codeBlock.CreateLocalVariable(SymbolTable.StringToId("#scope" + id), typeof(RubyScope)); MSA.LabelTarget loopLabel = Ast.Label(); gen.EnterBlockDefinition( codeBlock, - parameters[0], // BlockParam - selfVariable, // TODO: remove + blockParamVariable, + selfVariable, scopeVariable, loopLabel ); @@ -104,10 +111,13 @@ _definedScope.TransformLocals(codeBlock, gen); } - MSA.Expression paramInit = Ast.Block(MakeParametersInitialization(gen, selfVariable, parameters)); + MSA.Expression paramInit = Ast.Block(MakeParametersInitialization(gen, parameters)); MSA.VariableExpression blockUnwinder = codeBlock.CreateLocalVariable(SymbolTable.StringToId("#unwinder"), typeof(BlockUnwinder)); codeBlock.Body = Ast.Block( + // TODO: remove: + Ast.Assign(selfVariable, selfParameter), + paramInit, Ast.Infinite( @@ -128,8 +138,8 @@ gen.LeaveBlockDefinition(); return AstFactory.OpCall("DefineBlock", new MSA.Expression[] { - Ast.CodeContext(), - Ast.Read(gen.CurrentRfcVariable), + gen.CurrentScopeVariable, + gen.CurrentRfcVariable, gen.CurrentSelfVariable, AstFactory.LambdaExpression(codeBlock), Ast.Constant(_parameters.LeftValues.Count), @@ -138,15 +148,11 @@ }); } - private List/*!*/ MakeParametersInitialization(AstGenerator/*!*/ gen, MSA.VariableExpression/*!*/ selfVariable, MSA.ParameterExpression[]/*!*/ parameters) { - Assert.NotNull(gen, selfVariable); + private List/*!*/ MakeParametersInitialization(AstGenerator/*!*/ gen, MSA.ParameterExpression[]/*!*/ parameters) { + Assert.NotNull(gen); Assert.NotNullItems(parameters); List result = new List(); - - // TODO: remove - // self = #proc.Proc.self - result.Add(Ast.Write(selfVariable, AstFactory.OpCall("GetBlockSelf", parameters[0]))); // for each block parameter specified by the user, there is a formal parameter: int j = HiddenParameterCount; =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockReturnReason.cs;C402444 File: BlockReturnReason.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockReturnReason.cs;C402444 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/BlockReturnReason.cs;Procs5 @@ -60,12 +60,17 @@ internal ProcKind SourceProcKind { get { return _sourceProcKind; } } internal BlockReturnReason ReturnReason { get { return _returnReason; } set { _returnReason = value; } } internal RuntimeFlowControl TargetFrame { get { return _targetFrame; } } + public Proc/*!*/ Proc { get { return _proc; } } // emitted: - public Proc Proc { get { return _proc; } } + public object Self { get { return _proc.Self; } } - internal static PropertyInfo/*!*/ ProcProperty { get { return typeof(BlockParam).GetProperty("Proc"); } } + // emitted: + public int Id { get { return _proc.Id; } } + internal static PropertyInfo/*!*/ SelfProperty { get { return typeof(BlockParam).GetProperty("Self"); } } + internal static PropertyInfo/*!*/ IdProperty { get { return typeof(BlockParam).GetProperty("Id"); } } + private BlockParam(Proc/*!*/ proc, BlockCallerKind callerKind, bool isLibProcConverter) { _callerKind = callerKind; _proc = proc; @@ -101,18 +106,6 @@ #region Helpers - // For instance_eval/class_eval/module_eval, we want to temporarily change the block's self, - // but preserve its behavior with respect to control flow (and of course, not mutate the original Block). - // For this reason, we clone the whole BlockParam & Proc, but with a new self - public BlockParam/*!*/ CloneWithNewSelf(object newSelf) { - Proc newProc = new Proc(_proc.Kind, _proc.Info, newSelf); - newProc.Owner = _proc.Owner; - newProc.Converter = _proc.Converter; - BlockParam newBlock = new BlockParam(newProc, _callerKind, _isLibProcConverter); - newBlock.SetFlowControl(_returnReason, _targetFrame, newProc.Kind); - return newBlock; - } - // emitted: public object GetUnwinderResult(EvalUnwinder/*!*/ unwinder) { Debug.Assert(unwinder != null); @@ -180,9 +173,10 @@ case DynamicActionKind.Call: RuleBuilder result = new RuleBuilder(); ActionBinder binder = context.LanguageContext.Binder; - _proc.SetCallRule(result, binder, context, new CallArguments(args, result.Parameters, (CallAction)action), - Ast.ReadProperty(Ast.Convert(result.Parameters[0], ProcProperty.DeclaringType), ProcProperty) - ); + + SetCallActionRule(result, binder, context, new CallArguments(args, result.Parameters, (CallAction)action), + action is YieldAction); + return result; default: @@ -190,6 +184,42 @@ } } + /// + /// CallAction on Proc target. CallAction is used by yield. + /// From control flow perspective it "yields" to the proc. + /// + internal void SetCallActionRule(RuleBuilder/*!*/ rule, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, + CallArguments/*!*/ callActionArgs, bool isYieldAction) { + + Assert.NotNull(rule, binder, callActionArgs); + + // Call signature should be (BlockParam, [self:object], arguments): + Debug.Assert(callActionArgs.Length > 0 && typeof(BlockParam).IsAssignableFrom(callActionArgs.Values[0].GetType())); + Debug.Assert(!isYieldAction || callActionArgs.Length > 1); + + Expression blockParamExpression = Ast.Convert(callActionArgs.Expressions[0], typeof(BlockParam)); + Expression selfExpression = isYieldAction ? + callActionArgs.Expressions[1] : + Ast.ReadProperty(blockParamExpression, SelfProperty); + + // test for proc identity: + rule.AddTest(Ast.Equal(Ast.ReadProperty(blockParamExpression, IdProperty), Ast.Constant(_proc.Id))); + + // removes block param, self: + CallArguments yieldArgs = callActionArgs.RemoveAt(0); + if (isYieldAction) { + yieldArgs = yieldArgs.RemoveAt(0); + } + + // take the context the block is defined with: + _proc.Info.SetYieldRule(binder, callerContext, rule, + blockParamExpression, // block param + selfExpression, // self + yieldArgs, // user args + null // plain yield semantics, no transform + ); + } + #endregion } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C420856 File: Loader.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C420856 (server) 4/29/2008 6:15 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;Procs5 @@ -81,32 +81,6 @@ _loadedFiles = new RubyArray(); } - //public void LoadFile(CodeContext/*!*/ context, SourceUnit/*!*/ file) { - // LoadFile(context, file, _executionContext.ObjectClass); - //} - - //public void LoadFileIsolated(CodeContext/*!*/ context, SourceUnit/*!*/ file) { - // RubyModule module = _executionContext.CreateModule(SymbolId.Empty, null, null, null, null); - // LoadFile(context, file, module); - //} - - //public void LoadFile(CodeContext/*!*/ context, SourceUnit/*!*/ file, RubyModule/*!*/ module) { - // LoadFile(context, file, module, mainObject); - //} - - //public void LoadFile(CodeContext/*!*/ context, SourceUnit/*!*/ file, RubyModule/*!*/ module, object/*!*/ mainObject) { - // Assert.NotNull(file, module, topLevelSelf); - - // RubyCompilerOptions options = new RubyCompilerOptions(); - // options.IsMain = false; - - - - // RubyContext context = _executionContext.Context; - // ScriptCode compiledCode = file.Compile(options, _executionContext.RuntimeErrorSink); - // compiledCode.Run(context); - //} - /// /// Returns true if a Ruby file is successfully loaded, false if it is already loaded. /// =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C420856 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C420856 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;Procs5 @@ -85,12 +85,22 @@ Debug.Assert(parent is RubyScope, "The current code is loaded/required into Ruby code, this context is the load/require caller's one"); FunctionEnvironmentDictionary locals = new FunctionEnvironmentDictionary(storage, names); + // loader could be the caller of load, require or module_eval method: RubyScope loader = (RubyScope)parent; GlobalScopeExtension globalScope = loader.Top.RubyGlobalScope; - RubyScope result = new RubyTopLevelScope(globalScope, false, null, locals, names); - result.Initialize(new RuntimeFlowControl(), RubyMethodVisibility.Private, globalScope.MainObject); - result.SetDebugName("top-level"); + RubyScope result; + + // TODO: hack, see module_eval + if (loader is ModuleEvalScope) { + result = new RubyTopLevelScope(globalScope, false, (RubyModule)loader.SelfObject, locals, names); + result.Initialize(new RuntimeFlowControl(), RubyMethodVisibility.Public, loader.SelfObject); + result.SetDebugName("top-module-eval"); + } else { + result = new RubyTopLevelScope(globalScope, false, null, locals, names); + result.Initialize(new RuntimeFlowControl(), RubyMethodVisibility.Private, globalScope.MainObject); + result.SetDebugName("top-level"); + } return result; } @@ -222,13 +232,13 @@ #region Blocks // emitted: - public static Proc/*!*/ DefineBlock(CodeContext/*!*/ context, RuntimeFlowControl/*!*/ runtimeFlowControl, object/*!*/ self, Delegate/*!*/ clrMethod, + public static Proc/*!*/ DefineBlock(RubyScope/*!*/ scope, RuntimeFlowControl/*!*/ runtimeFlowControl, object self, Delegate/*!*/ clrMethod, int parameterCount, bool hasParamsArray, bool firstArgumentIsNestedLValue) { - Assert.NotNull(context, self, clrMethod); + Assert.NotNull(scope, clrMethod); // closes block over self and context - RubyBlockInfo info = new RubyBlockInfo(clrMethod, context, parameterCount, hasParamsArray, firstArgumentIsNestedLValue); - Proc result = new Proc(ProcKind.Block, info, self); + RubyBlockInfo info = new RubyBlockInfo(clrMethod, self, scope, parameterCount, hasParamsArray, firstArgumentIsNestedLValue); + Proc result = new Proc(ProcKind.Block, info); result.Owner = runtimeFlowControl; return result; @@ -939,11 +949,6 @@ return field; } - // emitted: (TODO: remove) - public static object GetBlockSelf(BlockParam/*!*/ proc) { - return proc.Proc.Self; - } - /// /// If this token is the last argument to a function, it indicates that we /// should perform a super call =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C422137 File: RubyScope.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C422137 (server) 4/29/2008 6:27 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;Procs5 @@ -269,4 +269,16 @@ //} } + + // TODO: remove (see module_eval) + public sealed class ModuleEvalScope : RubyScope { + public override ScopeKind Kind { + get { throw new NotImplementedException(); } + } + + public ModuleEvalScope(RubyScope/*!*/ loader, RubyModule/*!*/ self) + : base(loader, loader.Frame, loader.LocalNames) { + SelfObject = self; + } + } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBlockInfo.cs;C402444 File: RubyBlockInfo.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBlockInfo.cs;C402444 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyBlockInfo.cs;Procs5 @@ -28,31 +28,37 @@ namespace Ruby.Runtime.Calls { using Ast = Microsoft.Scripting.Ast.Expression; - internal sealed class RubyBlockInfo : RubyMemberInfo { - private const int MandatoryParamCount = 1; - + internal sealed class RubyBlockInfo { + // Self object captured by the block definition, if any. + // Although we could load self from scope in Ruby defined blocks, we cannot do so when we don't have a scope. + private readonly object _self; + + // Local scope inside the proc captured by the block definition; + // Null for block defined out of Ruby; + private readonly RubyScope _scope; + private readonly Delegate/*!*/ _method; private readonly int _optionalParamCount; private readonly bool _hasParamsArray; private readonly bool _firstArgumentIsNestedLValue; - private readonly CodeContext/*!*/ _context; - + public Delegate/*!*/ Method { get { return _method; } } public int OptionalParamCount { get { return _optionalParamCount; } } public bool HasParamsArray { get { return _hasParamsArray; } } - internal CodeContext/*!*/ Context { get { return _context; } } + internal object Self { get { return _self; } } + internal RubyScope LocalScope { get { return _scope; } } + // block: - internal RubyBlockInfo(Delegate/*!*/ method, CodeContext/*!*/ context, - int optional, bool hasParamsArray, bool firstArgumentIsNestedLValue) - : base(RubyMethodVisibility.Public, null) { - Assert.NotNull(method, context); - + internal RubyBlockInfo(Delegate/*!*/ method, object self, RubyScope scope, int parameterCount, bool hasParamsArray, bool firstArgumentIsNestedLValue) { + Assert.NotNull(method); + _method = method; - _context = context; - _optionalParamCount = optional; + _self = self; + _optionalParamCount = parameterCount; _hasParamsArray = hasParamsArray; _firstArgumentIsNestedLValue = firstArgumentIsNestedLValue; + _scope = scope; } private Expression/*!*/ AddWarning(Expression/*!*/ codeContextExpression, Expression/*!*/ expression) { @@ -66,36 +72,42 @@ return AstFactory.OpCall("MultipleValuesForBlockParameterWarning", codeContextExpression, expression); } - internal override void SetInvocationRule(string name, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, - RuleBuilder/*!*/ rule, CallArguments/*!*/ args) { + internal void SetYieldRule(ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, RuleBuilder/*!*/ rule, + Expression/*!*/ blockParameterExpression, // special arg #0 + Expression/*!*/ selfParameterExpression, // special arg #1 + CallArguments/*!*/ args, // user args, + Function actionTransform // a transform on the rule action + ) { + Assert.NotNull(binder, rule, blockParameterExpression, selfParameterExpression); + Assert.NotNull(args); - Assert.NotNull(binder, rule, args); + // user args are optional: + ArgsBuilder argsBuilder = new ArgsBuilder(0, Ruby.Compiler.Ast.BlockDefinition.HiddenParameterCount, _optionalParamCount, _hasParamsArray); - // actual values that will be passed to the callee (including special args): - ArgsBuilder argsBuilder; - Expression codeContextExpression = Ast.RuntimeConstant(_context); + Expression codeContextExpression = Ast.RuntimeConstant(callerContext); + SetCallRuleArguments(blockParameterExpression, selfParameterExpression, args, codeContextExpression, rule, argsBuilder); - // there are no implicit arguments: the BlockParam special argument is created at call-site and passed as an ordinary mandatory argument: - argsBuilder = new ArgsBuilder(0, MandatoryParamCount, _optionalParamCount, _hasParamsArray); + Expression action = AstFactory.ComplexCallHelper(_method, argsBuilder.GetArguments(), codeContextExpression); + if (actionTransform != null) { + action = actionTransform(action); + } - SetBlockInvocationRule(args, codeContextExpression, rule, argsBuilder); - - rule.Target = rule.MakeReturn(binder, AstFactory.ComplexCallHelper(_method, argsBuilder.GetArguments(), codeContextExpression)); + rule.Target = rule.MakeReturn(binder, action); } - private void SetBlockInvocationRule(CallArguments/*!*/ args, Expression/*!*/ codeContextExpression, - RuleBuilder/*!*/ rule, ArgsBuilder/*!*/ actualArgs) { + private void SetCallRuleArguments( + Expression/*!*/ blockParameterExpression, // special arg #0 + Expression/*!*/ selfParameterExpression, // special arg #1 + CallArguments/*!*/ args, // user args + Expression/*!*/ codeContextExpression, + RuleBuilder/*!*/ rule, + ArgsBuilder/*!*/ actualArgs) { - if (!args.Signature.IsSimple && (args.Length < MandatoryParamCount || args.GetArgumentKind(0) != ArgumentKind.Instance)) { - throw new InvalidOperationException("Invalid call site for a block. A call site needs to pass the " + typeof(BlockParam).Name + " object at least."); - } + // mandatory args: + actualArgs.Add(blockParameterExpression); + actualArgs.Add(selfParameterExpression); - // implicit (mandatory) args: int parameterIndex = 0; - while (parameterIndex < MandatoryParamCount) { - actualArgs.Add(args.Expressions[parameterIndex]); - parameterIndex++; - } // mimics CompoundLeftValue.TransformWrite // @@ -105,20 +117,20 @@ // L(0,*)? bool leftNoneSplat = _optionalParamCount == 0 && _hasParamsArray; - // R(0,-)? - bool rightNoneSplat = !args.Signature.IsSimple && args.Length == MandatoryParamCount + 1 && args.GetArgumentKind(MandatoryParamCount) == ArgumentKind.List; + // R(0,*)? + bool rightNoneSplat = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.List; // R(1,-)? - bool rightOneNone = !args.Signature.IsSimple && args.Length == MandatoryParamCount + 1 && args.GetArgumentKind(MandatoryParamCount) == ArgumentKind.Simple - || args.Signature.IsSimple && args.Length == MandatoryParamCount + 1; + bool rightOneNone = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.Simple + || args.Signature.IsSimple && args.Length == 1; // R(1,*)? - bool rightOneSplat = !args.Signature.IsSimple && args.Length == MandatoryParamCount + 2 && - args.GetArgumentKind(MandatoryParamCount) == ArgumentKind.Simple && - args.GetArgumentKind(MandatoryParamCount + 1) == ArgumentKind.List; + bool rightOneSplat = !args.Signature.IsSimple && args.Length == 2 && + args.GetArgumentKind(0) == ArgumentKind.Simple && + args.GetArgumentKind(1) == ArgumentKind.List; // R(0,-)? - bool rightNoneNone = args.Length == MandatoryParamCount; + bool rightNoneNone = args.Length == 0; if (leftOneNone) { Expression rvalue; =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs;C415805 File: RubyLambdaMethodInfo.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs;C415805 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyLambdaMethodInfo.cs;Procs5 @@ -42,61 +42,14 @@ internal override void SetInvocationRule(string name, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, RuleBuilder/*!*/ rule, CallArguments/*!*/ args) { - // TODO: hack, pass block as arg! - Proc boundLambda = _lambda.ToLambda(); - boundLambda.Self = args.Values[0]; + // removes instance (self): + CallArguments yieldArgs = args.RemoveAt(0); - // TODO: call SetProcCallRule directly (MRI invokes Proc#call implementation directly, it's not sending a message) - Expression[] newParams = ArrayUtils.MakeArray(args.Expressions); - newParams[0] = Ast.RuntimeConstant(boundLambda); - rule.Target = - rule.MakeReturn( - binder, - Ast.Action.InvokeMember( - binder, - SymbolTable.StringToId("call"), - typeof(object), - InvokeMemberActionFlags.IsCallWithThis, - args.Signature, - newParams - ) - ); + _lambda.SetProcCallRule(rule, binder, callerContext, + Ast.RuntimeConstant(_lambda), // proc object + args.Expressions[0], // self + yieldArgs // user args + ); } - - internal static void SetProcCallRule(ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, RuleBuilder/*!*/ rule, - CallArguments/*!*/ args) { - - Debug.Assert(args.Length > 0 && args.Values[0] != null && typeof(Proc).IsAssignableFrom(args.Values[0].GetType())); - - Proc proc = (Proc)args.Values[0]; - - // TODO (DLR): better rules composition - - Expression procParameter = rule.Parameters[0]; - VariableExpression bfcVariable = rule.GetTemporary(typeof(BlockParam), "#bfc"); - VariableExpression resultVariable = rule.GetTemporary(typeof(object), "#result"); - VariableExpression evalUnwinder = rule.GetTemporary(typeof(EvalUnwinder), "#result"); - - rule.Parameters[0] = Ast.Read(bfcVariable); // HACK - proc.SetCallRule(rule, binder, callerContext, args, procParameter); - - Expression invoke = ((ReturnStatement)rule.Target).Expression; - - rule.Target = Ast.Return(Ast.Comma( // HACK - Ast.Assign(bfcVariable, - Ast.Call( - BlockParam.GetMethod("CreateForProcCall"), - Ast.ConvertHelper(procParameter, typeof(Proc)) - ) - ), - Ast.Try( - Ast.Write(resultVariable, invoke) - ).Catch(typeof(EvalUnwinder), evalUnwinder, - Ast.Write(resultVariable, Ast.Call(Ast.Read(bfcVariable), BlockParam.GetMethod("GetUnwinderResult"), Ast.Read(evalUnwinder))) - ), - Ast.Call(RuntimeFlowControl.GetMethod("MethodProcCall"), Ast.Read(bfcVariable), Ast.Read(resultVariable)), - Ast.Read(resultVariable) - )); - } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RuleGenerators.cs;C400020 File: RuleGenerators.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RuleGenerators.cs;C400020 (server) 4/29/2008 12:31 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RuleGenerators.cs;Procs5 @@ -16,8 +16,11 @@ using Microsoft.Scripting.Actions; using Microsoft.Scripting.Runtime; using Ruby.Builtins; +using System.Diagnostics; namespace Ruby.Runtime.Calls { + using Ast = Microsoft.Scripting.Ast.Expression; + public delegate void RuleGenerator(string/*!*/ name, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, RuleBuilder/*!*/ result, CallArguments/*!*/ args); @@ -37,7 +40,7 @@ public static void ProcCall(string/*!*/ name, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, RuleBuilder/*!*/ rule, CallArguments/*!*/ args) { - RubyLambdaMethodInfo.SetProcCallRule(binder, callerContext, rule, args); + ((Proc)args.Values[0]).SetCallActionRule(rule, binder, callerContext, args); } public static void MethodCall(string/*!*/ name, ActionBinder/*!*/ binder, CodeContext/*!*/ callerContext, =================================================================== add: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/YieldAction.cs File: YieldAction.cs =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/YieldAction.cs;Procs5 @@ -1,0 +1,45 @@ +?/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Microsoft Public License. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Microsoft Public License, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Microsoft Public License. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Utils; + +namespace Ruby.Runtime.Calls { + public sealed class YieldAction : CallAction, IEquatable { + private YieldAction(ActionBinder/*!*/ binder, CallSignature/*!*/ callSignature) + : base(binder, callSignature) { + } + + public static new YieldAction/*!*/ Make(ActionBinder/*!*/ binder, CallSignature/*!*/ signature) { + ContractUtils.RequiresNotNull(binder, "binder"); + ContractUtils.RequiresNotNull(signature, "signature"); + + return new YieldAction(binder, signature); + } + + public static new YieldAction/*!*/ Make(ActionBinder/*!*/ binder, int argumentCount) { + ContractUtils.Requires(argumentCount >= 0, "argumentCount"); + ContractUtils.RequiresNotNull(binder, "binder"); + return new YieldAction(binder, new CallSignature(argumentCount)); + } + + public bool Equals(YieldAction other) { + return base.Equals(other); + } + } +} ===================================================================