edit: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/remove_method_spec.rb;C807294 File: remove_method_spec.rb =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/remove_method_spec.rb;C807294 (server) 2/11/2010 8:56 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/remove_method_spec.rb;SingletonOptimizations @@ -54,4 +54,14 @@ end }.should raise_error(NameError) end + + it "removes multiple methods one by one" do + M = Module.new do + def foo; end + def bar; end + + remove_method :foo, :undefined, :bar rescue 0 + instance_methods.map { |x| x.to_s }.should == ["bar"] + end + end end =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/undef_method_spec.rb;C807294 File: undef_method_spec.rb =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/undef_method_spec.rb;C807294 (server) 2/11/2010 8:56 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/External.LCA_RESTRICTED/Languages/IronRuby/mspec/rubyspec/core/module/undef_method_spec.rb;SingletonOptimizations @@ -83,3 +83,15 @@ ancestor.another_method_to_undef.should == 1 end end + +describe "Module#undef_method with multiple parameters" do + it "undefines methods one by one" do + M = Module.new do + def foo; end + def bar; end + + undef_method :foo, :undefined, :bar rescue 0 + instance_methods.map { |x| x.to_s }.should == ["bar"] + end + end +end =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby4.sln;C1584920 File: Ruby4.sln =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby4.sln;C1584920 (server) 2/10/2010 10:36 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby4.sln;SingletonOptimizations @@ -121,7 +121,6 @@ SccLocalPath13 = ..\\..\\Hosts\\SilverLight\\Microsoft.Scripting.SilverLight SccProvider13 = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} SccProjectUniqueName14 = ..\\..\\Hosts\\SilverLight\\Chiron\\Chiron.csproj - SccProjectName14 = ../../Hosts/SilverLight/Chiron SccAuxPath14 = http://vstfdevdiv:8080 SccLocalPath14 = ..\\..\\Hosts\\SilverLight\\Chiron SccProvider14 = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/ClassInitGenerator/Libraries/LibraryDef.cs;C1561136 File: LibraryDef.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/ClassInitGenerator/Libraries/LibraryDef.cs;C1561136 (server) 2/11/2010 8:04 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/ClassInitGenerator/Libraries/LibraryDef.cs;SingletonOptimizations @@ -192,8 +192,6 @@ get { return !IsExtension && ( QualifiedName == RubyClass.MainSingletonName - || QualifiedName == RubyClass.ClassSingletonName - || QualifiedName == RubyClass.ClassSingletonSingletonName || Extends == typeof(Kernel) || Extends == typeof(Object) || Extends == typeof(RubyClass) @@ -872,8 +870,6 @@ _output.WriteLine("Context.RegisterPrimitives("); _output.Indent++; - _output.WriteLine("Load{0}_Instance,", RubyClass.ClassSingletonName); - _output.WriteLine("Load{0}_Instance,", RubyClass.ClassSingletonSingletonName); _output.WriteLine("Load{0}_Instance,", RubyClass.MainSingletonName); _output.WriteLine(_moduleDefs[typeof(Kernel)].GetInitializerDelegates() + ","); =================================================================== add: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Experimental/Singletons/DummySingletons.rb File: DummySingletons.rb =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Experimental/Singletons/DummySingletons.rb;SingletonOptimizations @@ -1,0 +1,39 @@ +def get_singletons(cls, n) + result = [cls] + n.times do + cls = class << cls; self; end + result << cls + end + result +end + +class MetaModule < Module +end +MM = MetaModule.new + +[Object, Module, Class, MetaModule].each do |c| + s = class << c; self; end + + c.send(:define_method, :f) { c.name } + s.send(:define_method, :f) { 'S(' + c.name + ')' } +end + +[ + MM, + module M; self; end, + Module, + MetaModule +].each { |c| get_singletons(c, 10) } + +[ + MM, + module M; self; end, + Module, + MetaModule +].each do |c| + get_singletons(c, 3).each do |s| + printf '%-50s %s', s, s.f + puts + end + puts +end \ No newline at end of file =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Experimental/Singletons/Ultimate.MethodLookup.rb;C791094 File: Ultimate.MethodLookup.rb =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Experimental/Singletons/Ultimate.MethodLookup.rb;C791094 (server) 2/11/2010 10:36 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Experimental/Singletons/Ultimate.MethodLookup.rb;SingletonOptimizations @@ -53,8 +53,8 @@ $modules = [ module MyModule; self; end, module MyModule; class << self; self; end; end, - class MyClass; self; end, - class << Object.new; self; end, + class MyClass; self; end, + class << Object.new; self; end, ] def test met, x, y @@ -62,25 +62,25 @@ $modules.each { |m| puts "-- #{m} --------------" - m.module_eval do - puts send(met, *[:ai_k, :i_k][x,y]) rescue p $! - puts send(met, *[:ai_x, :i_x][x,y]) rescue p $! - puts send(met, *[:ai_o, :i_o][x,y]) rescue p $! - - # errors: - - puts send(met, *[:ac_k, :c_k][x,y]) rescue p $! - puts send(met, *[:ac_x, :c_x][x,y]) rescue p $! - puts send(met, *[:ac_o, :c_o][x,y]) rescue p $! - - puts send(met, *[:ai_c, :i_c][x,y]) rescue p $! - puts send(met, *[:ac_c, :c_c][x,y]) rescue p $! - puts send(met, *[:ai_m, :i_m][x,y]) rescue p $! - puts send(met, *[:ac_m, :c_m][x,y]) rescue p $! - - end - - puts + m.module_eval do + puts send(met, *[:ai_k, :i_k][x,y]) rescue p $! + puts send(met, *[:ai_x, :i_x][x,y]) rescue p $! + puts send(met, *[:ai_o, :i_o][x,y]) rescue p $! + + # errors: + + puts send(met, *[:ac_k, :c_k][x,y]) rescue p $! + puts send(met, *[:ac_x, :c_x][x,y]) rescue p $! + puts send(met, *[:ac_o, :c_o][x,y]) rescue p $! + + puts send(met, *[:ai_c, :i_c][x,y]) rescue p $! + puts send(met, *[:ac_c, :c_c][x,y]) rescue p $! + puts send(met, *[:ai_m, :i_m][x,y]) rescue p $! + puts send(met, *[:ac_m, :c_m][x,y]) rescue p $! + + end + + puts } end =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C1569144 File: RubyTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;C1569144 (server) 2/11/2010 11:32 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/RubyTests.cs;SingletonOptimizations @@ -600,6 +600,10 @@ AllowedSingletons1, AllowedSingletons2, SingletonMethodDefinitionOnSingletons1, + ModuleSingletons1, + ClassSingletons1, + DummySingletons1, + DummySingletons2, Super1, SuperParameterless1, =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/CloningTests.cs;C966724 File: CloningTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/CloningTests.cs;C966724 (server) 2/10/2010 7:32 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/CloningTests.cs;SingletonOptimizations @@ -39,15 +39,15 @@ x.instance_variable_set(:@iv_x, 4); y = x.clone - raise unless y.tainted? + raise '0' unless y.tainted? class << y - raise unless CONST == 1 # singleton constants copied - raise unless instance_variables.size == 0 # singleton instance variables not copied + raise '1' unless CONST == 1 # singleton constants copied + raise '2' unless instance_variables.size == 0 # singleton instance variables not copied end - raise unless y.foo == 3 # singleton methods copied - raise unless y.instance_variable_get(:@iv_x) == 4 # instance variables copied + raise '3' unless y.foo == 3 # singleton methods copied + raise '4' unless y.instance_variable_get(:@iv_x) == 4 # instance variables copied end "); }, @" =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;C1281974 File: ClrTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;C1281974 (server) 2/10/2010 11:02 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ClrTests.cs;SingletonOptimizations @@ -424,10 +424,11 @@ Context.ObjectClass.EnumerateConstants((module, name, value) => { RubyModule m = value as RubyModule; if (m != null && Array.IndexOf(irModules, m.Name) == -1) { + var s = m.GetOrCreateSingletonClass(); AssertNoClrNames(ModuleOps.GetInstanceMethods(m, true), m.Name); AssertNoClrNames(ModuleOps.GetPrivateInstanceMethods(m, true), m.Name); - AssertNoClrNames(ModuleOps.GetInstanceMethods(m.SingletonClass, true), m.Name); - AssertNoClrNames(ModuleOps.GetPrivateInstanceMethods(m.SingletonClass, true), m.Name); + AssertNoClrNames(ModuleOps.GetInstanceMethods(s, true), m.Name); + AssertNoClrNames(ModuleOps.GetPrivateInstanceMethods(s, true), m.Name); } return false; }); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/ModuleTests.cs;C1158858 edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SingletonTests.cs;C1544546 File: SingletonTests.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SingletonTests.cs;C1544546 (server) 2/11/2010 11:13 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/IronRuby.Tests/Runtime/SingletonTests.cs;SingletonOptimizations @@ -344,5 +344,148 @@ tfn "); } + + /// + /// Singleton(module)'s super-class is singleton(Module). + /// + public void ModuleSingletons1() { + TestOutput(@" +class Class + def self.c_c + :c_c + end +end + +class Module + def self.c_m + :c_m + end +end + +module M + class << self + $SM = self + end +end + +$SM.method(:c_c) rescue p $! +$SM.c_c rescue p $! + +p $SM.c_m +", @" +# +#> +:c_m +"); + } + + /// + /// Cannot instantiate singleton(class). + /// + public void ClassSingletons1() { + TestOutput(@" +class C; end +class << C + p method(:new) + new rescue p $! +end +", @" +# +# +"); + } + + private const string SingletonHelpers = @" +def get_singletons(cls, n) + result = [cls] + n.times do + cls = class << cls; self; end + result << cls + end + result +end +"; + + public void DummySingletons1() { + Engine.Execute(SingletonHelpers); + AssertOutput(() => + CompilerTest(@" +[ + class C; self; end, + module M; self; end, + class MM < Module; new; end +].each do |c| + get_singletons(c, 4).each do |s| + printf '%-50s %s', s, s.superclass rescue print $! + puts + end + puts +end +"), @" +C Object +# #> +#> #>> +#>> #>>> +#>>> #>>> + +undefined method `superclass' for M:Module +# #> +#> #>> +#>> #>>> +#>>> #>>> + +undefined method `superclass' for # +#> #>> +#>> #>>> +#>>> #>>>> +#>>>> #>>>> +", OutputFlags.Match); + } + + public void DummySingletons2() { + Engine.Execute(SingletonHelpers); + TestOutput(@" +class MetaModule < Module +end +MM = MetaModule.new + +[Object, Module, Class, MetaModule].each do |c| + s = class << c; self; end + + c.send(:define_method, :f) { c.name } + s.send(:define_method, :f) { 'S(' + c.name + ')' } +end + +[ + MM, + module M; self; end, + Module, + MetaModule +].each do |c| + get_singletons(c, 2).each do |s| # the results differ from MRI for other values than 2, it seems like a bug in MRI + printf '%-30s %s', s, s.f + puts + end + puts +end +", @" +MM MetaModule +# S(MetaModule) +#> S(MetaModule) + +M Module +# S(Module) +#> S(Module) + +Module S(Module) +# S(Class) +#> S(Class) + +MetaModule S(MetaModule) +# S(Class) +#> S(Class) +"); + } + } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C1589071 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C1589071 (server) 2/10/2010 2:40 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;SingletonOptimizations @@ -38,8 +38,6 @@ public sealed class BuiltinsLibraryInitializer : IronRuby.Builtins.LibraryInitializer { protected override void LoadModules() { Context.RegisterPrimitives( - Load__ClassSingleton_Instance, - Load__ClassSingletonSingleton_Instance, Load__MainSingleton_Instance, LoadKernel_Instance, LoadKernel_Class, null, LoadObject_Instance, LoadObject_Class, LoadObject_Constants, @@ -48,7 +46,6 @@ ); - // Skipped primitive: __ClassSingleton // Skipped primitive: __MainSingleton IronRuby.Builtins.RubyModule def49 = DefineGlobalModule("Comparable", typeof(IronRuby.Builtins.Comparable), 0x0000000F, LoadComparable_Instance, null, null, IronRuby.Builtins.RubyModule.EmptyArray); IronRuby.Builtins.RubyModule def40 = DefineGlobalModule("Enumerable", typeof(IronRuby.Builtins.Enumerable), 0x0000000F, LoadEnumerable_Instance, null, null, IronRuby.Builtins.RubyModule.EmptyArray); @@ -78,7 +75,6 @@ DefineGlobalModule("Signal", typeof(IronRuby.Builtins.Signal), 0x0000000F, null, LoadSignal_Class, null, IronRuby.Builtins.RubyModule.EmptyArray); #endif ExtendClass(typeof(System.Type), 0x00000000, null, LoadSystem__Type_Instance, null, null, IronRuby.Builtins.RubyModule.EmptyArray); - // Skipped primitive: __ClassSingletonSingleton #if !SILVERLIGHT object def1 = DefineSingleton(Load__Singleton_ArgFilesSingletonOps_Instance, null, null, def40); #endif @@ -362,60 +358,6 @@ SetBuiltinConstant(def2, "EXDEV", def17); } - private static void Load__ClassSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { - DefineLibraryMethod(module, "allocate", 0x51, - 0x00000000U, - new Action(IronRuby.Builtins.ClassSingletonOps.Allocate) - ); - - DefineLibraryMethod(module, "inherited", 0x52, - 0x00000002U, - new Action(IronRuby.Builtins.ClassSingletonOps.Inherited) - ); - - DefineLibraryMethod(module, "initialize", 0x52, - 0x00000000U, - new Func(IronRuby.Builtins.ClassSingletonOps.Initialize) - ); - - DefineLibraryMethod(module, "initialize_copy", 0x52, - 0x00000000U, - new Func(IronRuby.Builtins.ClassSingletonOps.InitializeCopy) - ); - - DefineLibraryMethod(module, "new", 0x51, - 0x00000000U, - new Action(IronRuby.Builtins.ClassSingletonOps.New) - ); - - DefineLibraryMethod(module, "superclass", 0x51, - 0x00000000U, - new Func(IronRuby.Builtins.ClassSingletonOps.GetSuperClass) - ); - - } - - private static void Load__ClassSingleton_Class(IronRuby.Builtins.RubyModule/*!*/ module) { - } - - private static void Load__ClassSingletonSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { - Load__ClassSingleton_Instance(module); - DefineLibraryMethod(module, "constants", 0x51, - 0x00000000U, - new Func(IronRuby.Builtins.ClassSingletonSingletonOps.GetConstants) - ); - - DefineLibraryMethod(module, "nesting", 0x51, - 0x00000000U, - new Func(IronRuby.Builtins.ClassSingletonSingletonOps.GetNesting) - ); - - } - - private static void Load__ClassSingletonSingleton_Class(IronRuby.Builtins.RubyModule/*!*/ module) { - Load__ClassSingleton_Class(module); - } - private static void Load__MainSingleton_Instance(IronRuby.Builtins.RubyModule/*!*/ module) { DefineLibraryMethod(module, "include", 0x51, 0x80000000U, @@ -4859,8 +4801,7 @@ ); DefineLibraryMethod(module, "extend_object", 0x52, - 0x00000002U, 0x00000000U, - new Func(IronRuby.Builtins.ModuleOps.ExtendObject), + 0x00000000U, new Func(IronRuby.Builtins.ModuleOps.ExtendObject) ); @@ -5031,8 +4972,8 @@ ); DefineLibraryMethod(module, "remove_method", 0x52, - 0x00010002U, - new Func(IronRuby.Builtins.ModuleOps.RemoveMethod) + 0x80010002U, + new Func(IronRuby.Builtins.ModuleOps.RemoveMethod) ); DefineLibraryMethod(module, "to_clr_ref", 0x51, @@ -5051,8 +4992,8 @@ ); DefineLibraryMethod(module, "undef_method", 0x52, - 0x00010002U, - new Func(IronRuby.Builtins.ModuleOps.UndefineMethod) + 0x80010002U, + new Func(IronRuby.Builtins.ModuleOps.UndefineMethod) ); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs;C1290543 File: ClassOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs;C1290543 (server) 2/11/2010 12:14 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ClassOps.cs;SingletonOptimizations @@ -13,12 +13,11 @@ * * ***************************************************************************/ -using System; +using System.Diagnostics; using System.Runtime.InteropServices; -using Microsoft.Scripting.Runtime; using IronRuby.Runtime; using IronRuby.Runtime.Calls; -using System.Reflection; +using Microsoft.Scripting.Runtime; namespace IronRuby.Builtins { @@ -70,7 +69,15 @@ [RubyMethod("superclass")] public static RubyClass GetSuperclass(RubyClass/*!*/ self) { - return self.SuperClass; + if (self.IsSingletonClass) { + RubyClass result = self.ImmediateClass; + Debug.Assert(result.IsSingletonClass); + + // do not return dummy singletons, also do not create a new singleton (MRI does): + return result.IsDummySingletonClass ? self : result; + } else { + return self.SuperClass; + } } [RubyMethod("clr_new")] =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;C1561136 File: KernelOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;C1561136 (server) 2/10/2010 2:01 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;SingletonOptimizations @@ -534,8 +534,10 @@ object self, [NotNull]RubyModule/*!*/ module, [NotNullItems]params RubyModule/*!*/[]/*!*/ modules) { Assert.NotNull(self, modules); - RubyUtils.RequireMixins(module.SingletonClass, modules); + // TODO: this is strange: + RubyUtils.RequireMixins(module.GetOrCreateSingletonClass(), modules); + var extendObject = extendObjectStorage.GetCallSite("extend_object", 1); var extended = extendedStorage.GetCallSite("extended", 1); @@ -640,7 +642,7 @@ public static object Evaluate(RubyScope/*!*/ scope, object self, [NotNull]MutableString/*!*/ code, [Optional, NotNull]MutableString file, [DefaultParameterValue(1)]int line) { - RubyClass singleton = scope.RubyContext.CreateSingletonClass(self); + RubyClass singleton = scope.RubyContext.GetOrCreateSingletonClass(self); return RubyUtils.Evaluate(code, scope, self, singleton, file, line); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C1561136 File: ModuleOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;C1561136 (server) 2/10/2010 2:01 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ModuleOps.cs;SingletonOptimizations @@ -68,17 +68,9 @@ // thread-safe: [RubyMethod("extend_object", RubyMethodAttributes.PrivateInstance)] - public static RubyModule/*!*/ ExtendObject(RubyModule/*!*/ self, [NotNull]RubyModule/*!*/ extendedModule) { - // include self into extendedModule's singleton class - extendedModule.SingletonClass.IncludeModules(self); - return self; - } - - // thread-safe: - [RubyMethod("extend_object", RubyMethodAttributes.PrivateInstance)] public static object ExtendObject(RubyModule/*!*/ self, object extendedObject) { // include self into extendedObject's singleton - self.Context.CreateSingletonClass(extendedObject).IncludeModules(self); + self.Context.GetOrCreateSingletonClass(extendedObject).IncludeModules(self); return extendedObject; } @@ -156,7 +148,7 @@ [RubyMethodAttribute("private_class_method")] public static RubyModule/*!*/ MakeClassMethodsPrivate(RubyModule/*!*/ self, [DefaultProtocol, NotNullItems]params string/*!*/[]/*!*/ methodNames) { - SetMethodAttributes(self.SingletonClass, methodNames, RubyMethodAttributes.Private); + SetMethodAttributes(self.GetOrCreateSingletonClass(), methodNames, RubyMethodAttributes.Private); return self; } @@ -164,7 +156,7 @@ [RubyMethodAttribute("public_class_method")] public static RubyModule/*!*/ MakeClassMethodsPublic(RubyModule/*!*/ self, [DefaultProtocol, NotNullItems]params string/*!*/[]/*!*/ methodNames) { - SetMethodAttributes(self.SingletonClass, methodNames, RubyMethodAttributes.Public); + SetMethodAttributes(self.GetOrCreateSingletonClass(), methodNames, RubyMethodAttributes.Public); return self; } @@ -211,7 +203,7 @@ method = module.ResolveMethodNoLock(methodName, VisibilityContext.AllVisible, options).Info; if (method == null) { - throw RubyExceptions.CreateNameError(RubyExceptions.FormatMethodMissingMessage(context, module, methodName)); + throw RubyExceptions.CreateUndefinedMethodError(module, methodName); } // MRI only adds method to the target module if visibility differs: @@ -229,7 +221,7 @@ } if (isModuleFunction) { - module.SingletonClass.MethodAdded(methodName); + module.GetOrCreateSingletonClass().MethodAdded(methodName); } } } @@ -469,25 +461,29 @@ // thread-safe: [RubyMethod("remove_method", RubyMethodAttributes.PrivateInstance)] - public static RubyModule/*!*/ RemoveMethod(RubyModule/*!*/ self, [DefaultProtocol, NotNull]string/*!*/ methodName) { + public static RubyModule/*!*/ RemoveMethod(RubyModule/*!*/ self, [DefaultProtocol, NotNullItems]params string[]/*!*/ methodNames) { // MRI 1.8: reports a warning and allows removal // MRI 1.9: throws a NameError - if (self == self.Context.ObjectClass && methodName == Symbols.Initialize) { - throw RubyExceptions.CreateNameError("Cannot remove Object#initialize"); + foreach (var methodName in methodNames) { + if (self == self.Context.ObjectClass && methodName == Symbols.Initialize) { + throw RubyExceptions.CreateNameError("Cannot remove Object#initialize"); + } + if (!self.RemoveMethod(methodName)) { + throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + } } - if (!self.RemoveMethod(methodName)) { - throw RubyExceptions.CreateUndefinedMethodError(self, methodName); - } return self; } // thread-safe: [RubyMethod("undef_method", RubyMethodAttributes.PrivateInstance)] - public static RubyModule/*!*/ UndefineMethod(RubyModule/*!*/ self, [DefaultProtocol, NotNull]string/*!*/ methodName) { - if (!self.ResolveMethod(methodName, VisibilityContext.AllVisible).Found) { - throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + public static RubyModule/*!*/ UndefineMethod(RubyModule/*!*/ self, [DefaultProtocol, NotNullItems]params string[]/*!*/ methodNames) { + foreach (var methodName in methodNames) { + if (!self.ResolveMethod(methodName, VisibilityContext.AllVisible).Found) { + throw RubyExceptions.CreateUndefinedMethodError(self, methodName); + } + self.UndefineMethod(methodName); } - self.UndefineMethod(methodName); return self; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs;C1561136 File: Precision.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs;C1561136 (server) 2/10/2010 2:22 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Precision.cs;SingletonOptimizations @@ -78,13 +78,14 @@ /// The host class including the module [RubyMethod("included", RubyMethodAttributes.PublicSingleton)] public static object Included(RubyContext/*!*/ context, RubyModule/*!*/ self, RubyModule/*!*/ includedIn) { - includedIn.SingletonClass.AddMethod( + var singleton = includedIn.GetOrCreateSingletonClass(); + singleton.AddMethod( context, "induced_from", new RubyLibraryMethodInfo( new[] { LibraryOverload.Create(new Func(InducedFrom), false, 0, 0) }, RubyMethodVisibility.Public, - includedIn.SingletonClass + singleton ) ); return self; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs;C1561136 File: SingletonOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs;C1561136 (server) 2/10/2010 2:06 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/SingletonOps.cs;SingletonOptimizations @@ -86,77 +86,4 @@ #endregion } - - /// - /// Methods on Singleton(class), Singleton(Singleton(object)). - /// - [RubyModule(RubyClass.ClassSingletonName)] - public static class ClassSingletonOps { - #region Private Instance Methods - - // Reinitialization. Not called when a factory/non-default ctor is called. - [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] - public static object/*!*/ Initialize(object/*!*/ self) { - // TODO: - throw new NotImplementedException("TODO"); - } - - [RubyMethod("initialize_copy", RubyMethodAttributes.PrivateInstance)] - public static object InitializeCopy(object/*!*/ self, object other) { - // TODO: - throw new NotImplementedException("TODO"); - } - - [RubyMethod("inherited", RubyMethodAttributes.PrivateInstance)] - public static void Inherited(object/*!*/ self, [NotNull]RubyClass/*!*/ subclass) { - // TODO: - throw new NotImplementedException("TODO"); - } - - #endregion - - #region Public Instance Methods - - [RubyMethod("allocate", RubyMethodAttributes.PublicInstance)] - public static void Allocate(RubyClass/*!*/ self) { - throw RubyExceptions.CreateTypeError("can't create instance of virtual class"); - } - - [RubyMethod("superclass", RubyMethodAttributes.PublicInstance)] - public static RubyClass/*!*/ GetSuperClass(RubyClass/*!*/ self) { - RubyClass result = self.SingletonClass; - Debug.Assert(result != null && result.IsSingletonClass); - - // do not return dummy singletons, also do not create a new singleton (MRI does): - return result.IsDummySingletonClass ? self : result; - } - - [RubyMethod("new", RubyMethodAttributes.PublicInstance)] - public static void New(RubyClass/*!*/ self) { - Allocate(self); - } - - #endregion - } - - /// - /// Methods on Singleton(Singleton(class)), Singleton(Singleton(Singleton(object))). - /// - [RubyModule(RubyClass.ClassSingletonSingletonName), Includes(typeof(ClassSingletonOps), Copy = true)] - public static class ClassSingletonSingletonOps { - - #region Public Instance Methods - - [RubyMethod("constants", RubyMethodAttributes.PublicInstance)] - public static RubyArray/*!*/ GetConstants(object/*!*/ self) { - throw new NotImplementedException("TODO"); - } - - [RubyMethod("nesting", RubyMethodAttributes.PublicInstance)] - public static RubyModule/*!*/ GetNesting(object/*!*/ self) { - throw new NotImplementedException("TODO"); - } - - #endregion - } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs;C1496258 File: IronRubyParseTreeOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs;C1496258 (server) 2/10/2010 2:39 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/ParseTree/IronRubyParseTreeOps.cs;SingletonOptimizations @@ -41,7 +41,7 @@ // bool includeNewLines = IncludeNewLines(module.Context, self); if (isClassMethod) { - module = module.SingletonClass; + module = module.ImmediateClass; } var member = module.GetMethod(methodName); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/LibraryInitializer.cs;C1561136 File: LibraryInitializer.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/LibraryInitializer.cs;C1561136 (server) 2/10/2010 2:01 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/LibraryInitializer.cs;SingletonOptimizations @@ -120,7 +120,7 @@ } object result = new RubyObject(_context.ObjectClass); - RubyClass singleton = _context.CreateInstanceSingleton(result, instanceTrait, classTrait, constantsInitializer, expandedMixins); + RubyClass singleton = _context.GetOrCreateInstanceSingleton(result, instanceTrait, classTrait, constantsInitializer, expandedMixins); return result; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C1586787 File: RubyClass.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C1586787 (server) 2/10/2010 11:07 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;SingletonOptimizations @@ -44,14 +44,16 @@ using AstUtils = Microsoft.Scripting.Ast.Utils; public sealed partial class RubyClass : RubyModule, IDuplicable { - public const string/*!*/ ClassSingletonName = "__ClassSingleton"; - public const string/*!*/ ClassSingletonSingletonName = "__ClassSingletonSingleton"; public const string/*!*/ MainSingletonName = "__MainSingleton"; // Level in class hierarchy (0 == Object) private readonly int _level; private readonly RubyClass _superClass; + // Lazy interlocked init. + // Created for classes that represent a subclass of Module class. + private RubyClass _dummySingletonClass; + // is this class a singleton class? private readonly bool _isSingletonClass; @@ -226,6 +228,25 @@ get { return _singletonClassOf; } } + internal void InitializeDummySingleton() { + Debug.Assert(_dummySingletonClass == null); + _dummySingletonClass = CreateDummySingleton(); + } + + internal RubyClass/*!*/ GetDummySingletonClass() { + if (_dummySingletonClass == null) { + Debug.Assert(IsSubclassOf(Context.ModuleClass)); + Interlocked.CompareExchange(ref _dummySingletonClass, CreateDummySingleton(), null); + } + return _dummySingletonClass; + } + + private RubyClass/*!*/ CreateDummySingleton() { + var result = new RubyClass(Context, null, null, this, null, null, null, this.ImmediateClass, null, null, null, false, true, ModuleRestrictions.None); + result.InitializeImmediateClass(result); + return result; + } + // A class defined in Ruby code (not libraries, CLR types) public bool IsRubyClass { get { return _isRubyClass; } @@ -271,8 +292,7 @@ public RubyClass(RubyClass/*!*/ rubyClass) : this(rubyClass.Context, null, null, null, null, null, null, rubyClass.Context.ObjectClass, null, null, null, true, false, ModuleRestrictions.None) { - // all modules need a singleton (see RubyContext.CreateModule): - InitializeDummySingletonClass(rubyClass, null); + InitializeImmediateClass(rubyClass, null); } // friend: RubyContext @@ -550,7 +570,7 @@ if (!IsSingletonClass) { // singleton members are copied here, not in InitializeCopy: - result.SingletonClass.InitializeMembersFrom(SingletonClass); + result.ImmediateClass.InitializeMembersFrom(ImmediateClass); // copy instance variables and taint flag: Context.CopyInstanceData(this, result, false); @@ -581,6 +601,9 @@ return false; } + /// + /// Returns true if this class is equal to super or it is its descendant. + /// public bool IsSubclassOf(RubyClass/*!*/ super) { Assert.NotNull(super); @@ -1230,7 +1253,10 @@ } public void BuildObjectConstructionNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { - Debug.Assert(!IsSingletonClass, "Cannot instantiate singletons"); + if (IsSingletonClass) { + metaBuilder.SetError(Methods.MakeVirtualClassInstantiatedError.OpCall()); + return; + } Type type = GetUnderlyingSystemType(); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.Meta.cs;C1107696 File: RubyClass.Meta.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.Meta.cs;C1107696 (server) 2/10/2010 11:08 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.Meta.cs;SingletonOptimizations @@ -46,7 +46,7 @@ var names = new List(); using (Context.ClassHierarchyLocker()) { - Value.SingletonClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, (name, module, member) => names.Add(name)); + Value.ImmediateClass.ForEachMember(true, RubyMethodAttributes.DefaultVisibility, (name, module, member) => names.Add(name)); } return names; =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyModule.cs;C1496258 File: RubyModule.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyModule.cs;C1496258 (server) 2/10/2010 10:47 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyModule.cs;SingletonOptimizations @@ -99,6 +99,8 @@ } private readonly RubyContext/*!*/ _context; + + #region CLR Types and Namespaces // the namespace this module represents or null: private readonly NamespaceTracker _namespaceTracker; @@ -107,17 +109,119 @@ // TODO: unify with _underlyingSystemType on classes private readonly TypeTracker _typeTracker; + public TypeTracker TypeTracker { + get { return _typeTracker; } + } + + public NamespaceTracker NamespaceTracker { + get { return _namespaceTracker; } + } + + public bool IsInterface { + get { return _typeTracker != null && _typeTracker.Type.IsInterface; } + } + + public bool IsClrModule { + get { return _typeTracker != null && IsModuleType(_typeTracker.Type); } + } + + public virtual Type/*!*/ GetUnderlyingSystemType() { + if (IsClrModule) { + return _typeTracker.Type; + } else { + throw new InvalidOperationException(); + } + } + + #endregion + private readonly ModuleRestrictions _restrictions; private readonly WeakReference/*!*/ _weakSelf; // name of the module or null for anonymous modules: private string _name; - // lazy interlocked init: - private RubyClass _singletonClass; + // Lazy interlocked init'd. + private RubyInstanceData _instanceData; + + #region Immediate/Singleton/Super Class - private RubyInstanceData _instanceData; + // Lazy interlocked init'd. + // Classes + // Null immediately after construction, initialized by RubyContext.CreateClass factory to a singleton class. + // This lazy initialization is needed to allow circular references among Kernel, Object, Module and Class. + // We create the singleton class eagerly in the factory since classes' singleton classes form a hierarchy parallel + // to the main inheritance hierarachy of classes. If we didn't we would need to update super-references of all singleton subclasses + // that were created after a singleton class is lazily created. + // Modules + // Initialized to the class of the module and may later be changed to a singleton class (only if the singleton is needed). + // We don't create the singleton class eagerly to optimize rule generation. If we did, each (meta)module instance would receive its own immediate class + // different from other instances of the (meta)module. And thus the instances would use different method table versions eventhough they are the same. + // Singleton Classes + // Self reference for dummy singletons (tha last singletons in the singleton chain). + private RubyClass _immediateClass; + /// + /// A dummy singleton class is an immutable class that has no members and is used to terminate singleton class chain. + /// + /// + /// A method invoked on the last class in the singleton class chain before the dummy singleton is searched in the inheritance hierarchy of the dummy singleton: + /// [dummy singleton, singleton(Class), singleton(Module), singleton(Object), Class, Module, Object]. + /// The ImmediateClass reference cannot be null (that would cause null-ref exception in rules), so we need to set it to the dummy. + /// + public bool IsDummySingletonClass { + get { return _immediateClass == this; } + } + + public virtual bool IsSingletonClass { + get { return false; } + } + + public virtual bool IsClass { + get { return false; } + } + + public bool IsObjectClass { + get { return ReferenceEquals(this, Context.ObjectClass); } + } + + public bool IsComClass { + get { return ReferenceEquals(this, Context.ComObjectClass); } + } + + internal virtual RubyClass GetSuperClass() { + return null; + } + + // thread safe: + internal void InitializeImmediateClass(RubyClass/*!*/ cls) { + Debug.Assert(_immediateClass == null); + _immediateClass = cls; + } + + // thread safe: + internal void InitializeImmediateClass(RubyClass/*!*/ singletonSuperClass, Action trait) { + Assert.NotNull(singletonSuperClass); + + RubyClass immediate; + if (IsClass) { + // class: eager singleton class construction: + immediate = CreateSingletonClass(singletonSuperClass, trait); + immediate.InitializeImmediateClass(_context.ClassClass.GetDummySingletonClass()); + } else if (trait != null) { + // module: eager singleton class construction: + immediate = CreateSingletonClass(singletonSuperClass, trait); + immediate.InitializeImmediateClass(singletonSuperClass.GetDummySingletonClass()); + } else { + // module: lazy singleton class construction: + immediate = singletonSuperClass; + } + + InitializeImmediateClass(immediate); + } + + #endregion + #region Mutable state guarded by ClassHierarchyLock [Emitted] @@ -216,45 +320,6 @@ #endregion - public TypeTracker TypeTracker { - get { return _typeTracker; } - } - - public NamespaceTracker NamespaceTracker { - get { return _namespaceTracker; } - } - - public bool IsInterface { - get { return _typeTracker != null && _typeTracker.Type.IsInterface; } - } - - public bool IsClrModule { - get { return _typeTracker != null && IsModuleType(_typeTracker.Type); } - } - - public virtual Type/*!*/ GetUnderlyingSystemType() { - if (IsClrModule) { - return _typeTracker.Type; - } else { - throw new InvalidOperationException(); - } - } - - public RubyClass/*!*/ SingletonClass { - get { - Debug.Assert(_singletonClass != null); - return _singletonClass; - } - } - - public bool IsDummySingletonClass { - get { return ReferenceEquals(_singletonClass, this); } - } - - public virtual bool IsSingletonClass { - get { return false; } - } - public ModuleRestrictions Restrictions { get { return _restrictions; } } @@ -272,22 +337,6 @@ get { return _context; } } - public virtual bool IsClass { - get { return false; } - } - - public bool IsObjectClass { - get { return ReferenceEquals(this, Context.ObjectClass); } - } - - public bool IsComClass { - get { return ReferenceEquals(this, Context.ComObjectClass); } - } - - internal virtual RubyClass GetSuperClass() { - return null; - } - internal virtual RubyGlobalScope GlobalScope { get { return null; } } @@ -297,16 +346,16 @@ } // default allocator: - public RubyModule(RubyClass/*!*/ rubyClass) - : this(rubyClass, null) { + public RubyModule(RubyClass/*!*/ metaModuleClass) + : this(metaModuleClass, null) { } - // creates an empty module: + // creates an empty (meta)module: protected RubyModule(RubyClass/*!*/ metaModuleClass, string name) : this(metaModuleClass.Context, name, null, null, null, null, null, ModuleRestrictions.None) { - - // all modules need a singleton (see RubyContext.CreateModule): - InitializeDummySingletonClass(metaModuleClass, null); + + // metaModuleClass represents a subclass of Module or its duplicate (Kernel#dup) + InitializeImmediateClass(metaModuleClass, null); } internal RubyModule(RubyContext/*!*/ context, string name, Action methodsInitializer, Action constantsInitializer, @@ -548,16 +597,20 @@ } object IDuplicable.Duplicate(RubyContext/*!*/ context, bool copySingletonMembers) { - // the meta-module class of this module is the singleton's super class: - RubyModule result = new RubyModule(_singletonClass.SuperClass, null); - + + // capture the current immediate class (it can change any time if it not a singleton class) + RubyClass immediate = _immediateClass; + + RubyModule result = new RubyModule(immediate.IsSingletonClass ? immediate.SuperClass : immediate, null); + // singleton members are copied here, not in InitializeCopy: - if (copySingletonMembers && !IsSingletonClass) { + if (copySingletonMembers && immediate.IsSingletonClass) { + var singletonClass = result.GetOrCreateSingletonClass(); using (Context.ClassHierarchyLocker()) { - result.SingletonClass.InitializeMembersFrom(SingletonClass); + singletonClass.InitializeMembersFrom(immediate); } } - + // copy instance variables: _context.CopyInstanceData(this, result, false); return result; @@ -569,6 +622,7 @@ // A version of a frozen module can still change if its super-classes/mixins change. private void Mutate() { + Debug.Assert(!IsDummySingletonClass); if (IsFrozen) { throw RubyExceptions.CreateTypeError(String.Format("can't modify frozen {0}", IsClass ? "class" : "module")); } @@ -664,11 +718,10 @@ // thread-safe: public RubyClass ImmediateClass { get { - return _singletonClass; + return _immediateClass; } set { - // all modules have singleton classes initialized at the creation time, these cannot be changed: - throw Assert.Unreachable; + throw new InvalidOperationException("Cannot change the immediate class of a module"); } } @@ -730,31 +783,42 @@ } // thread safe: - public RubyClass/*!*/ CreateSingletonClass() { - Debug.Assert(!IsDummySingletonClass); + public RubyClass/*!*/ GetOrCreateSingletonClass() { + if (IsDummySingletonClass) { + throw new InvalidOperationException("Dummy singleton class has no singleton class"); + } - var singleton = _singletonClass; - if (singleton.IsDummySingletonClass) { - RubyClass super = ((RubyModule)singleton.SingletonClassOf).IsClass ? Context.ClassClass.SingletonClass : Context.ModuleClass.SingletonClass; - RubyClass newDummy = CreateDummySingletonClass(super, Context.SingletonSingletonTrait); - - // update singleton only if it still points to itself: - Interlocked.CompareExchange(ref singleton._singletonClass, newDummy, singleton); + RubyClass immediate = _immediateClass; + RubyClass singletonSuper; + RubyClass singletonImmediate; + + if (!immediate.IsSingletonClass) { + // finish module singleton initialization: + Debug.Assert(!IsClass); + singletonSuper = immediate; + singletonImmediate = immediate.GetDummySingletonClass(); + } else if (immediate.IsDummySingletonClass) { + // expanding singleton chain: + singletonSuper = immediate.SuperClass; + singletonImmediate = immediate; + } else { + return immediate; } - Debug.Assert(_singletonClass.IsSingletonClass && !_singletonClass.IsDummySingletonClass); - return _singletonClass; - } + var singleton = CreateSingletonClass(singletonSuper, null); + singleton.InitializeImmediateClass(singletonImmediate); + Interlocked.CompareExchange(ref _immediateClass, singleton, immediate); - // thread safe: - internal void InitializeDummySingletonClass(RubyClass/*!*/ superClass, Action trait) { - // if multiple threads are trying to set the singleton, the first one should win: - var previous = Interlocked.CompareExchange(ref _singletonClass, CreateDummySingletonClass(superClass, trait), null); - Debug.Assert(previous == null); + Debug.Assert(_immediateClass.IsSingletonClass && !_immediateClass.IsDummySingletonClass); + return _immediateClass; } - // thread safe: - private RubyClass/*!*/ CreateDummySingletonClass(RubyClass/*!*/ superClass, Action trait) { + /// + /// Create a new singleton class for this module. + /// Doesn't attach this module to it yet, the caller needs to do so. + /// + /// Thread safe. + internal RubyClass/*!*/ CreateSingletonClass(RubyClass/*!*/ superClass, Action trait) { // Note that in MRI, member tables of dummy singleton are shared with the class the dummy is singleton for // This is obviously an implementation detail leaking to the language and we don't support that. @@ -770,9 +834,6 @@ #if DEBUG result.Version.SetName(result.DebugName); #endif - result._singletonClass = result; - - // MRI: return result; } @@ -1222,7 +1283,8 @@ public void SetModuleFunctionNoEventNoLock(RubyContext/*!*/ callerContext, string/*!*/ name, RubyMemberInfo/*!*/ method) { // CLR members: Detaches the member from its underlying type (by creating a copy). // TODO: check for CLR instance members, it should be an error to call module_function on them: - SingletonClass.SetMethodNoEventNoLock(callerContext, name, method.Copy(RubyMemberFlags.Public, SingletonClass)); + var singletonClass = GetOrCreateSingletonClass(); + singletonClass.SetMethodNoEventNoLock(callerContext, name, method.Copy(RubyMemberFlags.Public, singletonClass)); } // thread-safe: @@ -1949,7 +2011,8 @@ } if (classTrait != null) { - SingletonClass.IncludeTraitNoLock(ref SingletonClass._methodsInitializer, SingletonClass._methodsState, classTrait); + var singleton = GetOrCreateSingletonClass(); + singleton.IncludeTraitNoLock(ref singleton._methodsInitializer, singleton._methodsState, classTrait); } // updates the module version: @@ -1980,7 +2043,7 @@ RubyModule module = singletonOf as RubyModule; - if (module == null) { + if (module == null || !module.IsSingletonClass && module.Name == null) { nestings++; result.Append("#<"); result.Append(c.SuperClass.GetName(context)); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;C1561136 File: RubyStruct.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;C1561136 (server) 2/10/2010 2:05 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyStruct.cs;SingletonOptimizations @@ -148,13 +148,15 @@ var newInstance = new RuleGenerator(RuleGenerators.InstanceConstructor); var context = cls.Context; - cls.SingletonClass.AddMethod(context, "[]", new RubyCustomMethodInfo(newInstance, RubyMemberFlags.Public, cls.SingletonClass)); - cls.SingletonClass.AddMethod(context, "new", new RubyCustomMethodInfo(newInstance, RubyMemberFlags.Public, cls.SingletonClass)); + var singletonClass = cls.GetOrCreateSingletonClass(); - cls.SingletonClass.AddMethod(context, "members", new RubyLibraryMethodInfo( + singletonClass.AddMethod(context, "[]", new RubyCustomMethodInfo(newInstance, RubyMemberFlags.Public, singletonClass)); + singletonClass.AddMethod(context, "new", new RubyCustomMethodInfo(newInstance, RubyMemberFlags.Public, singletonClass)); + + singletonClass.AddMethod(context, "members", new RubyLibraryMethodInfo( new[] { LibraryOverload.Create(new Func(GetMembers), false, 0, 0) }, - RubyMemberFlags.Public, - cls.SingletonClass + RubyMemberFlags.Public, + singletonClass )); for (int i = 0; i < structMembers.Length; i++) { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;C1569144 File: ReflectionCache.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;C1569144 (server) 2/11/2010 12:00 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Compiler/ReflectionCache.Generated.cs;SingletonOptimizations @@ -402,6 +402,8 @@ private static MethodInfo _MakeTopLevelSuperException; public static MethodInfo/*!*/ MakeTypeConversionError { get { return _MakeTypeConversionError ?? (_MakeTypeConversionError = CallInstruction.CacheFunc(RubyOps.MakeTypeConversionError)); } } private static MethodInfo _MakeTypeConversionError; + public static MethodInfo/*!*/ MakeVirtualClassInstantiatedError { get { return _MakeVirtualClassInstantiatedError ?? (_MakeVirtualClassInstantiatedError = CallInstruction.CacheFunc(RubyOps.MakeVirtualClassInstantiatedError)); } } + private static MethodInfo _MakeVirtualClassInstantiatedError; public static MethodInfo/*!*/ MakeWrongNumberOfArgumentsError { get { return _MakeWrongNumberOfArgumentsError ?? (_MakeWrongNumberOfArgumentsError = CallInstruction.CacheFunc(RubyOps.MakeWrongNumberOfArgumentsError)); } } private static MethodInfo _MakeWrongNumberOfArgumentsError; public static MethodInfo/*!*/ MarkException { get { return _MarkException ?? (_MarkException = CallInstruction.CacheFunc(RubyOps.MarkException)); } } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyContext.cs;C1561136 File: RubyContext.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyContext.cs;C1561136 (server) 2/10/2010 10:49 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyContext.cs;SingletonOptimizations @@ -242,8 +242,6 @@ private RubyClass _standardErrorClass; private RubyClass _comObjectClass; - private Action/*!*/ _classSingletonTrait; - private Action/*!*/ _singletonSingletonTrait; private Action/*!*/ _mainSingletonTrait; // internally set by Initializer: @@ -268,9 +266,6 @@ } } - internal Action/*!*/ ClassSingletonTrait { get { return _classSingletonTrait; } } - internal Action/*!*/ SingletonSingletonTrait { get { return _singletonSingletonTrait; } } - // Set of names that method_missing defined on any module was resolved for and that are cached. Lazy init. // // Note: We used to have this set per module but that doesn't work - see unit test MethodCallCaching_MethodMissing4. @@ -611,8 +606,6 @@ // TODO: internal public void RegisterPrimitives( - Action/*!*/ classSingletonTrait, - Action/*!*/ singletonSingletonTrait, Action/*!*/ mainSingletonTrait, Action/*!*/ kernelInstanceTrait, @@ -631,12 +624,10 @@ Action/*!*/ classClassTrait, Action classConstantsInitializer) { - Assert.NotNull(classSingletonTrait, singletonSingletonTrait, mainSingletonTrait); + Assert.NotNull(mainSingletonTrait); Assert.NotNull(objectInstanceTrait, kernelInstanceTrait, moduleInstanceTrait, classInstanceTrait); Assert.NotNull(objectClassTrait, kernelClassTrait, moduleClassTrait, classClassTrait); - _classSingletonTrait = classSingletonTrait; - _singletonSingletonTrait = singletonSingletonTrait; _mainSingletonTrait = mainSingletonTrait; // inheritance hierarchy: @@ -669,12 +660,20 @@ _objectClass = new RubyClass(this, Symbols.Object, objectTracker.Type, null, objectInstanceTrait, objectConstantsInitializer, null, null, new[] { _kernelModule }, objectTracker, null, false, false, ModuleRestrictions.Builtin & ~ModuleRestrictions.NoOverrides); _moduleClass = new RubyClass(this, Symbols.Module, typeof(RubyModule), null, moduleInstanceTrait, moduleConstantsInitializer, moduleFactories, _objectClass, null, null, null, false, false, ModuleRestrictions.Builtin); _classClass = new RubyClass(this, Symbols.Class, typeof(RubyClass), null, classInstanceTrait, classConstantsInitializer, classFactories, _moduleClass, null, null, null, false, false, ModuleRestrictions.Builtin); + + _objectClass.InitializeImmediateClass(_objectClass.CreateSingletonClass(_classClass, objectClassTrait)); + _moduleClass.InitializeImmediateClass(_moduleClass.CreateSingletonClass(_objectClass.ImmediateClass, moduleClassTrait)); + _classClass.InitializeImmediateClass(_classClass.CreateSingletonClass(_moduleClass.ImmediateClass, classClassTrait)); - _kernelModule.InitializeDummySingletonClass(_moduleClass, kernelClassTrait); - _objectClass.InitializeDummySingletonClass(_classClass, objectClassTrait); - _moduleClass.InitializeDummySingletonClass(_objectClass.SingletonClass, moduleClassTrait); - _classClass.InitializeDummySingletonClass(_moduleClass.SingletonClass, classClassTrait); + _moduleClass.InitializeDummySingleton(); + _classClass.InitializeDummySingleton(); + _objectClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass()); + _moduleClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass()); + _classClass.ImmediateClass.InitializeImmediateClass(_classClass.GetDummySingletonClass()); + + _kernelModule.InitializeImmediateClass(_moduleClass, kernelClassTrait); + _objectClass.SetConstantNoMutateNoLock(_moduleClass.Name, _moduleClass); _objectClass.SetConstantNoMutateNoLock(_classClass.Name, _classClass); _objectClass.SetConstantNoMutateNoLock(_objectClass.Name, _objectClass); @@ -877,6 +876,9 @@ #region Class and Module Factories (thread-safe) + /// + /// Class factory. Do not use RubyClass constructor except for special cases (Object, Class, Module, singleton classes). + /// internal RubyClass/*!*/ CreateClass(string name, Type type, object classSingletonOf, Action instanceTrait, Action classTrait, Action constantsInitializer, Delegate/*!*/[] factories, RubyClass/*!*/ superClass, RubyModule/*!*/[] expandedMixins, TypeTracker tracker, RubyStruct.Info structInfo, @@ -888,10 +890,13 @@ isRubyClass, isSingletonClass, restrictions ); - result.InitializeDummySingletonClass(superClass.SingletonClass, classTrait); + result.InitializeImmediateClass(superClass.ImmediateClass, classTrait); return result; } + /// + /// Module factory. Do not use RubyModule constructor except special cases (Kernel). + /// internal RubyModule/*!*/ CreateModule(string name, Action instanceTrait, Action classTrait, Action constantsInitializer, RubyModule/*!*/[] expandedMixins, NamespaceTracker namespaceTracker, TypeTracker typeTracker, ModuleRestrictions restrictions) { @@ -899,27 +904,28 @@ RubyModule result = new RubyModule( this, name, instanceTrait, constantsInitializer, expandedMixins, namespaceTracker, typeTracker, restrictions ); - result.InitializeDummySingletonClass(_moduleClass, classTrait); + + result.InitializeImmediateClass(_moduleClass, classTrait); return result; } /// /// Creates a singleton class for specified object unless it already exists. /// - public RubyClass/*!*/ CreateSingletonClass(object obj) { + public RubyClass/*!*/ GetOrCreateSingletonClass(object obj) { RubyModule module = obj as RubyModule; if (module != null) { - return module.CreateSingletonClass(); + return module.GetOrCreateSingletonClass(); } - return CreateInstanceSingleton(obj, null, null, null, null); + return GetOrCreateInstanceSingleton(obj, null, null, null, null); } - internal RubyClass/*!*/ CreateMainSingleton(object obj, RubyModule/*!*/[] expandedMixins) { - return CreateInstanceSingleton(obj, _mainSingletonTrait, null, null, expandedMixins); + internal RubyClass/*!*/ GetOrCreateMainSingleton(object obj, RubyModule/*!*/[] expandedMixins) { + return GetOrCreateInstanceSingleton(obj, _mainSingletonTrait, null, null, expandedMixins); } - internal RubyClass/*!*/ CreateInstanceSingleton(object obj, Action instanceTrait, Action classTrait, + internal RubyClass/*!*/ GetOrCreateInstanceSingleton(object obj, Action instanceTrait, Action classTrait, Action constantsInitializer, RubyModule/*!*/[] expandedMixins) { Debug.Assert(!(obj is RubyModule)); Debug.Assert(RubyUtils.HasSingletonClass(obj)); @@ -940,7 +946,7 @@ } RubyClass result = CreateClass( - null, null, obj, instanceTrait, classTrait ?? _classSingletonTrait, constantsInitializer, null, + null, null, obj, instanceTrait, classTrait, constantsInitializer, null, immediate, expandedMixins, null, null, true, true, ModuleRestrictions.None ); @@ -2384,7 +2390,7 @@ } RubyObject mainObject = new RubyObject(_objectClass); - RubyClass mainSingleton = CreateMainSingleton(mainObject, null); + RubyClass mainSingleton = GetOrCreateMainSingleton(mainObject, null); RubyGlobalScope result = new RubyGlobalScope(this, globalScope, mainObject, createHosted); if (bindGlobals) { =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C1479709 File: RubyExceptionData.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C1479709 (server) 2/10/2010 2:21 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;SingletonOptimizations @@ -405,7 +405,7 @@ RubyClass exceptionClass = context.GetClass(exception.GetType()); // new resolves to Class#new built-in method: - var newMethod = exceptionClass.SingletonClass.ResolveMethod("new", VisibilityContext.AllVisible); + var newMethod = exceptionClass.ImmediateClass.ResolveMethod("new", VisibilityContext.AllVisible); if (newMethod.Found && newMethod.Info.DeclaringModule == context.ClassClass && newMethod.Info is RubyCustomMethodInfo) { // initialize resolves to a built-in method: var initializeMethod = exceptionClass.ResolveMethod("initialize", VisibilityContext.AllVisible); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptions.cs;C1544575 File: RubyExceptions.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptions.cs;C1544575 (server) 2/11/2010 10:48 AM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptions.cs;SingletonOptimizations @@ -99,6 +99,11 @@ } public static Exception/*!*/ CreateUndefinedMethodError(RubyModule/*!*/ module, string/*!*/ methodName) { + // MRI doesn't display the singleton's name: + if (module.IsSingletonClass) { + module = ((RubyClass)module).GetNonSingletonClass(); + } + return CreateNameError("undefined method `{0}' for {2} `{1}'", methodName, module.Name, module.IsClass ? "class" : "module"); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C1496258 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C1496258 (server) 2/10/2010 2:01 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;SingletonOptimizations @@ -504,7 +504,7 @@ instanceOwner = null; instanceFlags = RubyMemberFlags.Invalid; - singletonOwner = scope.RubyContext.CreateSingletonClass(target); + singletonOwner = scope.RubyContext.GetOrCreateSingletonClass(target); singletonFlags = RubyMemberFlags.Public; } else { var attributesScope = scope.GetMethodAttributesDefinitionScope(); @@ -527,7 +527,7 @@ } instanceFlags = RubyMemberFlags.Private; - singletonOwner = instanceOwner.SingletonClass; + singletonOwner = instanceOwner.GetOrCreateSingletonClass(); singletonFlags = RubyMemberFlags.Public; moduleFunction = true; } else { @@ -559,7 +559,7 @@ if (moduleFunction) { Debug.Assert(!method.DeclaringModule.IsClass); - method.DeclaringModule.SingletonClass.MethodAdded(body.Name); + method.DeclaringModule.GetOrCreateSingletonClass().MethodAdded(body.Name); } return null; @@ -642,7 +642,7 @@ if (!RubyUtils.HasSingletonClass(obj)) { throw RubyExceptions.CreateTypeError(String.Format("no virtual class for {0}", scope.RubyContext.GetClassOf(obj).Name)); } - return scope.RubyContext.CreateSingletonClass(obj); + return scope.RubyContext.GetOrCreateSingletonClass(obj); } [Emitted] @@ -1768,6 +1768,11 @@ } [Emitted] + public static Exception/*!*/ MakeVirtualClassInstantiatedError() { + return RubyExceptions.CreateTypeError("can't create instance of virtual class"); + } + + [Emitted] public static Exception/*!*/ MakeAbstractMethodCalledError(RuntimeMethodHandle/*!*/ method) { return new NotImplementedException(String.Format("Abstract method `{0}' not implemented", MethodInfo.GetMethodFromHandle(method))); } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C1561136 File: RubyScope.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;C1561136 (server) 2/10/2010 2:01 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyScope.cs;SingletonOptimizations @@ -1052,7 +1052,7 @@ RubyModule module = context.CreateModule(null, null, null, null, null, null, null, ModuleRestrictions.None); RubyObject mainObject = new RubyObject(context.ObjectClass); - context.CreateMainSingleton(mainObject, new[] { module }); + context.GetOrCreateMainSingleton(mainObject, new[] { module }); RubyTopLevelScope scope = new RubyTopLevelScope(rubyGlobalScope, module, null, mainObject); scope.SetDebugName("top-level-wrapped"); =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C1561136 File: RubyUtils.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C1561136 (server) 2/8/2010 3:12 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;SingletonOptimizations @@ -885,7 +885,7 @@ } object result; - EvaluateBlock(block, block.RubyContext.CreateSingletonClass(self), self, args, out result); + EvaluateBlock(block, block.RubyContext.GetOrCreateSingletonClass(self), self, args, out result); return result; } =================================================================== edit: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMetaBinder.cs;C1475764 File: RubyMetaBinder.cs =================================================================== --- $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMetaBinder.cs;C1475764 (server) 2/8/2010 4:17 PM +++ Shelved Change: $/Dev10/feature/vs_langs01_s/Merlin/Main/Languages/Ruby/Ruby/Runtime/Calls/RubyMetaBinder.cs;SingletonOptimizations @@ -19,19 +19,20 @@ using Microsoft.Scripting.Ast; #endif +using System; +using System.Collections.Generic; using System.Dynamic; using System.Diagnostics; -using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; using Microsoft.Scripting.Utils; -using System.Collections.Generic; -using AstUtils = Microsoft.Scripting.Ast.Utils; -using System.Reflection; using Microsoft.Scripting; +using Microsoft.Scripting.Interpreter; using Microsoft.Scripting.Runtime; -using System.Runtime.CompilerServices; -using Microsoft.Scripting.Interpreter; namespace IronRuby.Runtime.Calls { + using AstUtils = Microsoft.Scripting.Ast.Utils; using Ast = Expression; public abstract class RubyMetaBinder : DynamicMetaObjectBinder, ILightCallSiteBinder, IExpressionSerializable { @@ -84,6 +85,22 @@ return null; } +#if DEBUG + if (RubyOptions.ShowRules) { + var sb = new StringBuilder(); + for (int i = 1; i < args.Length; i++) { + sb.Append(RubyUtils.ObjectToMutableString(context, args[i])); + sb.Append(", "); + } + + Utils.Log(String.Format( + "{0}: {1}; {2}", + this, + sb, + args.Length > 1 ? context.GetClassOf(args[1]).DebugName : null + ), "BIND"); + } +#endif result = this.LightBind(site, args, context.Options.CompilationThreshold); CacheTarget(result); return result; ===================================================================