edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C444795 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C444795 (server) 5/22/2008 3:26 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;InstanceEval @@ -313,17 +313,22 @@ Eval1, Eval2, LocalNames1, + LocalNames2, + // TODO: fix module scope to generate its own ScopeExpression LocalNames3, Binding1, TopLevelBinding_RubyProgram, EvalWithProcBinding1, ModuleEvalProc1, ModuleEvalProc2, ModuleEvalProc3, + ModuleInstanceEvalProc3, ModuleClassNew1, ModuleClassNew2, ModuleEvalString1, + InstanceEvalString1, ModuleEvalString2, - ModuleEvalString3, + InstanceEvalString2, + ModuleInstanceEvalString3, SuperEval1, // TODO: SuperParameterlessEval1, =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;C438696 File: EvalTests.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;C438696 (server) 5/22/2008 3:23 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/EvalTests.cs;InstanceEval @@ -70,7 +70,7 @@ CompilerTest(@" def f a = 1 - puts local_variables + puts local_variables.sort b = 2 end @@ -81,6 +81,54 @@ b"); } + public void LocalNames2() { + AssertOutput(delegate() { + CompilerTest(@" +def bar + 1.times { |x| + y = 1 + 1.times { |z| + $b = binding + } + } +end + +bar + +eval('puts local_variables.sort', $b) +"); + }, @" +x +y +z +"); + } + + public void LocalNames3() { + AssertOutput(delegate() { + CompilerTest(@" +def bar + 1.times { |x| + eval('module M + y = 1 + 1.times { |z| + $b = binding + } + end'); + } +end + +bar + +eval('puts local_variables.sort', $b) +"); + }, @" +x +y +z +"); + } + public void Binding1() { AssertOutput(delegate() { CompilerTest(@" @@ -177,6 +225,44 @@ "); } + public void ModuleInstanceEvalProc3() { + AssertOutput(delegate() { + CompilerTest(@" +class C + D = 1 +end + +C.module_eval { + p Module.nesting + puts D rescue puts 'error' + + def foo + puts 'foo' + end +} + +C.instance_eval { + p Module.nesting + puts D rescue puts 'error' + + def bar + puts 'bar' + end +} + +C.new.foo +C.bar +"); + }, @" +[] +error +[] +error +foo +bar +"); + } + /// /// module_eval uses yield semantics for invoking the proc. /// (return in a yield to inactive lambda doesn't work). @@ -251,7 +337,6 @@ "); } - public void ModuleEvalString1() { AssertOutput(delegate() { CompilerTest(@" @@ -268,6 +353,26 @@ "); } + public void InstanceEvalString1() { + AssertOutput(delegate() { + CompilerTest(@" +module N + module M + class << self + C = 123 + end + end +end + +N::M.instance_eval('p self, C, Module.nesting') +"); + }, @" +N::M +123 +[#] +"); + } + public void ModuleEvalString2() { AssertOutput(delegate() { CompilerTest(@" @@ -280,9 +385,21 @@ }, @"1"); } - public void ModuleEvalString3() { + public void InstanceEvalString2() { AssertOutput(delegate() { CompilerTest(@" +class C +end + +C.instance_eval('def foo; puts 1; end') +C.foo +"); + }, @"1"); + } + + public void ModuleInstanceEvalString3() { + AssertOutput(delegate() { + CompilerTest(@" module M end @@ -293,11 +410,14 @@ def foo a = 1 M.module_eval('puts a') + M.instance_eval('puts a') end foo "); - }, @"1"); + }, @" +1 +1"); } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C447029 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C447029 (server) 5/22/2008 4:23 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;InstanceEval @@ -2293,6 +2293,7 @@ }); module.DefineLibraryMethod("instance_eval", 0x29, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.InstanceEval), }); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C447029 File: Kernel.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C447029 (server) 5/22/2008 2:58 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;InstanceEval @@ -151,14 +151,14 @@ [RubyMethod("eval", RubyMethodAttributes.PublicSingleton)] public static object Evaluate(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ code, [Optional]Binding binding, [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { - return RubyUtils.Evaluate(self, code, (binding != null) ? binding.LocalScope : RubyUtils.GetScope(context), file, line); + return RubyUtils.Evaluate(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, [NotNull]Proc/*!*/ procBinding, [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { - return RubyUtils.Evaluate(self, code, procBinding.LocalScope, file, line); + return RubyUtils.Evaluate(code, procBinding.LocalScope, file, line); } //exec @@ -715,29 +715,17 @@ } #endregion -#if TODO - static DynamicSite/*!*/ _InstanceEvalSite = CallSiteFactory.CreateSimpleCallSite(RubyContext.RubyBinder); + [RubyMethod("instance_eval")] + public static object Evaluate(CodeContext/*!*/ context, object self, BlockParam block, [NotNull]MutableString/*!*/ code, + [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { - [RubyMethod("instance_eval")] - public static object InstanceEval(CodeContext/*!*/ context, object self, BlockParam block) { - if (block == null) { - throw RubyExceptions.CreateArgumentError("block not supplied"); + // TODO: binder should do this: + if (block != null) { + throw RubyExceptions.CreateArgumentError("wrong number of arguments"); } - // TODO: for modules/classes, this has different semantics. For example: - // class Test; end - // Test.instance_eval { def test_instance_eval; end } - // Test.class_eval { def test_class_eval; end } - // p Test.instance_methods(false) # => ["test_instance_eval"] - // p Test.singleton_methods(false) # => ["test_class_eval"] - if (self is RubyModule) { - throw RubyExceptions.CreateNotImplementedError("instance_eval on Module or Class needs singleton support"); - } - BlockParam newBlock = block.CloneWithNewSelf(self); - return _InstanceEvalSite.Invoke(context, newBlock); - throw new NotImplementedException("TODO"); + return RubyUtils.Evaluate(code, RubyUtils.GetScope(context), self, null, file, line); } -#endif private static DynamicSite _InstanceEvalSite = DynamicSite.Create(YieldAction.Make(RubyContext.RubyBinder, 2)); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C447029 File: ModuleOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C447029 (server) 5/22/2008 4:09 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;InstanceEval @@ -601,7 +601,7 @@ throw RubyExceptions.CreateArgumentError("wrong number of arguments"); } - return RubyUtils.EvaluateInModule((RubyScope)context, self, code, file, line); + return RubyUtils.Evaluate(code, RubyUtils.GetScope(context), self, self, file, line); } [RubyMethod("module_eval")] =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Parser/LexicalScope.cs;C429806 edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C447029 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C447029 (server) 5/22/2008 3:14 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;InstanceEval @@ -114,14 +114,14 @@ // emitted: public static CodeContext/*!*/ CreateEvalScope(ILocalVariables/*!*/ locals, CodeContext/*!*/ parent, bool isVisible) { - // TODO: hack, see module_eval - if (parent is ModuleEvalScope) { - ModuleEvalScope modEvalScope = (ModuleEvalScope)parent; - RubyScope callerScope = (RubyScope)modEvalScope.Parent; + // TODO: hack, see module_eval, instance_eval + EvalScopeHolder evalScope = parent as EvalScopeHolder; + if (evalScope != null) { + RubyScope callerScope = (RubyScope)evalScope.Parent; - RubyModuleScope result = new RubyModuleScope(callerScope, locals); - result.Initialize(callerScope.RuntimeFlowControl, callerScope.MethodAttributes, modEvalScope.SelfObject); - result.SetDebugName("top-module-eval"); + RubyModuleScope result = new RubyModuleScope(callerScope, locals, evalScope.Module, true); + result.Initialize(callerScope.RuntimeFlowControl, callerScope.MethodAttributes, evalScope.SelfObject); + result.SetDebugName("top-module/instance-eval"); return result; } else { @@ -153,7 +153,7 @@ // emitted: public static CodeContext/*!*/ CreateModuleScope(ILocalVariables/*!*/ locals, CodeContext/*!*/ parent, bool isVisible) { RubyScope p = (RubyScope)parent; - return new RubyModuleScope(p, p.Frame); + return new RubyModuleScope(p, p.Frame, null, false); } // TODO: remove @@ -166,6 +166,7 @@ RubyScope parent = (RubyScope)scope.Parent; result.Initialize(rfc, RubyMethodAttributes.PublicInstance, module); + result.SetModule(module); result.SetDebugName((module.IsClass ? "class" : "module") + " " + SymbolTable.IdToString(module.Name)); // TODO: flow from parent, we might want to remove the fields from block scope: @@ -243,11 +244,7 @@ // emitted: public static object GetLocalVariable(RubyScope/*!*/ scope, SymbolId name) { - object result; - while (!scope.Frame.TryGetValue(name, out result) && scope.Parent != null) { - scope = (RubyScope)scope.Parent; - } - return result; + return scope.ResolveLocalVariable(name); } // emitted: =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C443395 File: RubyScope.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C443395 (server) 5/22/2008 3:12 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;InstanceEval @@ -50,6 +50,7 @@ private RubyMethodAttributes _methodAttributes; public abstract ScopeKind Kind { get; } + public abstract bool InheritsLocalVariables { get; } public virtual RubyModule Module { get { return null; } @@ -176,14 +177,36 @@ // TODO: public List/*!*/ GetVisibleLocalNames() { List result = new List(); + RubyScope scope = this; + while (true) { + foreach (string name in scope.LocalNames) { + if (!name.StartsWith("#")) { + result.Add(SymbolTable.StringToId(name)); + } + } - foreach (string name in LocalNames) { - if (!name.StartsWith("#")) { - result.Add(SymbolTable.StringToId(name)); + if (!scope.InheritsLocalVariables) { + return result; } + + scope = (RubyScope)scope.Parent; } + } - return result; + internal object ResolveLocalVariable(SymbolId name) { + RubyScope scope = this; + while (true) { + object result; + if (scope.Frame.TryGetValue(name, out result)) { + return result; + } + + if (!scope.InheritsLocalVariables) { + return null; + } + + scope = (RubyScope)scope.Parent; + } } #region Debug View @@ -217,7 +240,7 @@ } } - if (scope.Kind != ScopeKind.Block) { + if (!scope.InheritsLocalVariables) { break; } scope = (RubyScope)scope.Parent; @@ -231,9 +254,10 @@ get { return _scope._selfObject; } } + [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] [DebuggerDisplay("{A2 != null ? A2.ToString() : \"nil\",nq}", Name = "$~", Type = "{_matchClassName,nq}")] public Match A2 { - get { return _scope._currentMatch.Match; } + get { return (_scope._currentMatch != null) ? _scope._currentMatch.Match : null; } } [DebuggerDisplay("{B}", Name = "MethodAttributes", Type = "")] @@ -283,6 +307,7 @@ private Proc _blockParameter; public override ScopeKind Kind { get { return ScopeKind.Method; } } + public override bool InheritsLocalVariables { get { return false; } } public RubyMethodInfo Method { get { return _method; } @@ -300,19 +325,27 @@ } public sealed class RubyModuleScope : RubyScope { + // TODO: readonly + private RubyModule _module; + private readonly bool _isEval; + public override ScopeKind Kind { get { return ScopeKind.Module; } } + public override bool InheritsLocalVariables { get { return _isEval; } } - public override RubyModule Module { - get { return (RubyModule)SelfObject; } - } + public override RubyModule Module { get { return _module; } } - internal RubyModuleScope(RubyScope/*!*/ parent, ILocalVariables/*!*/ frame) + internal void SetModule(RubyModule/*!*/ module) { _module = module; } + + internal RubyModuleScope(RubyScope/*!*/ parent, ILocalVariables/*!*/ frame, RubyModule module, bool isEval) : base(parent, frame) { + _module = module; + _isEval = isEval; } } public sealed class RubyBlockScope : RubyScope { public override ScopeKind Kind { get { return ScopeKind.Block; } } + public override bool InheritsLocalVariables { get { return true; } } // TODO: readonly private BlockParam _blockParam; @@ -329,6 +362,7 @@ public sealed class RubyTopLevelScope : RubyScope { public override ScopeKind Kind { get { return ScopeKind.TopLevel; } } + public override bool InheritsLocalVariables { get { return false; } } private readonly bool _isMain; private readonly GlobalScopeExtension/*!*/ _globalScope; @@ -395,15 +429,18 @@ } - // TODO: remove (see module_eval) - public sealed class ModuleEvalScope : RubyScope { - public override ScopeKind Kind { - get { throw new NotImplementedException(); } - } + // TODO: remove (see module_eval, instance_eval) + public sealed class EvalScopeHolder : RubyScope { + private readonly RubyModule _module; - public ModuleEvalScope(RubyScope/*!*/ loader, RubyModule/*!*/ self) + public override ScopeKind Kind { get { throw new NotImplementedException(); } } + public override bool InheritsLocalVariables { get { throw new NotImplementedException(); } } + public override RubyModule Module { get { return _module; } } + + public EvalScopeHolder(RubyScope/*!*/ loader, object self, RubyModule module) : base(loader, loader.Frame) { SelfObject = self; + _module = module; } } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C445425 File: RubyUtils.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C445425 (server) 5/22/2008 3:02 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;InstanceEval @@ -493,7 +493,7 @@ } } - public static object Evaluate(object self, MutableString/*!*/ code, RubyScope/*!*/ targetScope, MutableString file, int line) { + public static object Evaluate(MutableString/*!*/ code, RubyScope/*!*/ targetScope, MutableString file, int line) { Assert.NotNull(code, targetScope); RubyExecutionContext ec = targetScope.ExecutionContext; @@ -511,22 +511,27 @@ return compiledCode.Run(targetScope, false); } - public static object EvaluateInModule(RubyScope/*!*/ currentScope, RubyModule/*!*/ self, MutableString/*!*/ code, MutableString file, int line) { + public static object Evaluate(MutableString/*!*/ code, RubyScope/*!*/ targetScope, object self, RubyModule module, MutableString file, int line) { Assert.NotNull(self, code); - RubyExecutionContext ec = currentScope.ExecutionContext; + RubyExecutionContext ec = targetScope.ExecutionContext; + // singleton class for instance eval: + if (module == null) { + module = ec.CreateSingletonClass(self); + } + // we want to create a new top-level local scope: RubyCompilerOptions options = new RubyCompilerOptions(); options.IsEval = true; - options.LocalNames = currentScope.GetVisibleLocalNames(); + options.LocalNames = targetScope.GetVisibleLocalNames(); SourceUnit source = ec.Context.CreateSnippet(code.ConvertToString()); ScriptCode compiledCode = source.Compile(options, ec.RuntimeErrorSink); Debug.Assert(compiledCode != null); // TODO: this is hack - we need arbitrary signature of the Initialize method: - ModuleEvalScope moduleEvalScope = new ModuleEvalScope(currentScope, self); + EvalScopeHolder moduleEvalScope = new EvalScopeHolder(targetScope, self, module); return compiledCode.Run(moduleEvalScope, false); } =================================================================== delete: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/Specs/core/kernel/.spec/instance_eval_excludes.txt;C422137 File: instance_eval_excludes.txt =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/Specs/core/kernel/.spec/instance_eval_excludes.txt;C422137 (server) 5/22/2008 5:14 PM +++ [no target file] @@ -1,4 +1,0 @@ -Kernel#instance_eval passes the object to the block -Kernel#instance_eval binds self to the receiver -Kernel#instance_eval executes in the context of the receiver -Kernel#instance_eval has access to receiver's instance variables ===================================================================