add: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby.vsmdi File: Ruby.vsmdi =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby.vsmdi;exc @@ -1,0 +1,6 @@ +? + + + + + \ No newline at end of file =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby.vssscc;C390406 edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C761655 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C761655 (server) 3/2/2009 12:40 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;exc @@ -1005,7 +1005,7 @@ module.DefineRuleGenerator("exception", 0x51, IronRuby.Builtins.ExceptionOps.GetException()); module.DefineLibraryMethod("initialize", 0x52, - new System.Func(IronRuby.Builtins.ExceptionOps.ReinitializeException) + new System.Func(IronRuby.Builtins.ExceptionOps.InitializeException) ); module.DefineLibraryMethod("inspect", 0x51, @@ -2340,7 +2340,7 @@ module.DefineLibraryMethod("fail", 0x52, new System.Action(IronRuby.Builtins.KernelOps.RaiseException), new System.Action(IronRuby.Builtins.KernelOps.RaiseException), - new System.Action(IronRuby.Builtins.KernelOps.RaiseException) + new System.Action(IronRuby.Builtins.KernelOps.RaiseException) ); module.DefineLibraryMethod("Float", 0x52, @@ -2524,7 +2524,7 @@ module.DefineLibraryMethod("raise", 0x52, new System.Action(IronRuby.Builtins.KernelOps.RaiseException), new System.Action(IronRuby.Builtins.KernelOps.RaiseException), - new System.Action(IronRuby.Builtins.KernelOps.RaiseException) + new System.Action(IronRuby.Builtins.KernelOps.RaiseException) ); module.DefineLibraryMethod("rand", 0x52, @@ -2707,7 +2707,7 @@ module.DefineLibraryMethod("fail", 0x61, new System.Action(IronRuby.Builtins.KernelOps.RaiseException), new System.Action(IronRuby.Builtins.KernelOps.RaiseException), - new System.Action(IronRuby.Builtins.KernelOps.RaiseException) + new System.Action(IronRuby.Builtins.KernelOps.RaiseException) ); module.DefineLibraryMethod("Float", 0x61, @@ -2801,7 +2801,7 @@ module.DefineLibraryMethod("raise", 0x61, new System.Action(IronRuby.Builtins.KernelOps.RaiseException), new System.Action(IronRuby.Builtins.KernelOps.RaiseException), - new System.Action(IronRuby.Builtins.KernelOps.RaiseException) + new System.Action(IronRuby.Builtins.KernelOps.RaiseException) ); module.DefineLibraryMethod("rand", 0x61, @@ -5107,7 +5107,7 @@ module.DefineLibraryMethod("raise", 0x51, new System.Action(IronRuby.Builtins.ThreadOps.RaiseException), new System.Action(IronRuby.Builtins.ThreadOps.RaiseException), - new System.Action(IronRuby.Builtins.ThreadOps.RaiseException) + new System.Action(IronRuby.Builtins.ThreadOps.RaiseException) ); #if !SILVERLIGHT =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;C759835 File: ExceptionOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;C759835 (server) 3/2/2009 10:45 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ExceptionOps.cs;exc @@ -23,6 +23,7 @@ using System.Dynamic; using System.Diagnostics; using System.Reflection; +using IronRuby.Compiler; using IronRuby.Compiler.Generation; using Ast = System.Linq.Expressions.Expression; @@ -72,16 +73,15 @@ } [Emitted] - public static Exception/*!*/ ReinitializeException(Exception/*!*/ self, object/*!*/ message) { + public static Exception/*!*/ InitializeException(Exception/*!*/ self, object/*!*/ message) { var instance = RubyExceptionData.GetInstance(self); - instance.Backtrace = null; instance.Message = message; return self; } [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)] - public static Exception/*!*/ ReinitializeException(RubyContext/*!*/ context, Exception/*!*/ self, [DefaultParameterValue(null)]object message) { - return ReinitializeException(self, message ?? context.GetClassOf(self).Name); + public static Exception/*!*/ InitializeException(RubyContext/*!*/ context, Exception/*!*/ self, [DefaultParameterValue(null)]object message) { + return InitializeException(self, message ?? context.GetClassOf(self).Name); } [RubyMethod("exception", RubyMethodAttributes.PublicSingleton)] @@ -128,20 +128,30 @@ } else { RubyClass cls = args.RubyContext.GetClassOf(args.Target); var classExpression = AstUtils.Constant(cls); - args.SetTarget(classExpression, cls); + ParameterExpression messageVariable = metaBuilder.GetTemporary(typeof(object), "#message"); + Expression clrMessage = Ast.Call( + null, + new Func(GetClrMessage).Method, + classExpression, + Ast.Assign(messageVariable, AstFactory.Box(argsBuilder[0]))); - ParameterExpression messageVariable = null; + // if (self == null) + // + // else if (self == message) + // self + // else + // #message = + // new (GetClrMessage(#message)) - // ReinitializeException(new (GetClrMessage(, #message = )), #message) - metaBuilder.Result = Ast.Call(null, new Func(ReinitializeException).Method, - cls.MakeAllocatorCall(args, () => - Ast.Call(null, new Func(GetClrMessage).Method, - classExpression, - Ast.Assign(messageVariable = metaBuilder.GetTemporary(typeof(object), "#message"), AstFactory.Box(argsBuilder[0])) - ) - ), - messageVariable ?? AstFactory.Box(argsBuilder[0]) - ); + metaBuilder.Result = Ast.Condition( + Ast.Call(typeof(object).GetMethod("ReferenceEquals"), args.TargetExpression, argsBuilder[0]), + Ast.Convert(args.TargetExpression, typeof(Exception)), + Ast.Call( + typeof(RubyOps).GetMethod("InitializeException"), + cls.MakeAllocatorCall(args, classExpression, clrMessage), + AstFactory.Box(argsBuilder[0]), + Ast.Constant(false)), + typeof(Exception)); } } }); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;C761655 File: KernelOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;C761655 (server) 3/2/2009 2:45 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/KernelOps.cs;exc @@ -711,10 +711,10 @@ [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] [RubyStackTraceHidden] - public static void RaiseException(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, + public static void RaiseException(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, SetBacktraceStorage/*!*/ setBackTraceStorage, RubyContext/*!*/ context, object self, object/*!*/ obj, [Optional]object arg, [Optional]RubyArray backtrace) { - Exception exception = CreateExceptionToRaise(respondToStorage, storage0, storage1, context, obj, arg, backtrace); + Exception exception = CreateExceptionToRaise(respondToStorage, storage0, storage1, setBackTraceStorage, context, obj, arg, backtrace); #if DEBUG && !SILVERLIGHT if (RubyOptions.UseThreadAbortForSyncRaise) { RubyUtils.RaiseAsyncException(Thread.CurrentThread, exception); @@ -724,7 +724,7 @@ throw exception; } - internal static Exception CreateExceptionToRaise(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, + internal static Exception CreateExceptionToRaise(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, SetBacktraceStorage/*!*/ setBackTraceStorage, RubyContext/*!*/ context, object/*!*/ obj, object arg, RubyArray backtrace) { if (Protocols.RespondTo(respondToStorage, context, obj, "exception")) { @@ -739,7 +739,7 @@ if (e != null) { if (backtrace != null) { - ExceptionOps.SetBacktrace(e, backtrace); + RubyExceptionData.GetInstance(e).SetBacktrace(setBackTraceStorage, context, backtrace); } return e; } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;C700904 File: ThreadOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;C700904 (server) 3/1/2009 11:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/ThreadOps.cs;exc @@ -25,6 +25,7 @@ using IronRuby.Runtime; using System.Text; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace IronRuby.Builtins { /// @@ -442,18 +443,18 @@ [RubyMethod("raise")] [RubyStackTraceHidden] - public static void RaiseException(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, + public static void RaiseException(RespondToStorage/*!*/ respondToStorage, UnaryOpStorage/*!*/ storage0, BinaryOpStorage/*!*/ storage1, SetBacktraceStorage/*!*/ setBackTraceStorage, RubyContext/*!*/ context, Thread/*!*/ self, object/*!*/ obj, [Optional]object arg, [Optional]RubyArray backtrace) { if (self == Thread.CurrentThread) { - KernelOps.RaiseException(respondToStorage, storage0, storage1, context, self, obj, arg, backtrace); + KernelOps.RaiseException(respondToStorage, storage0, storage1, setBackTraceStorage, context, self, obj, arg, backtrace); return; } #if SILVERLIGHT throw new NotImplementedError("Thread#raise is not implemented on Silverlight"); #else - Exception e = KernelOps.CreateExceptionToRaise(respondToStorage, storage0, storage1, context, obj, arg, backtrace); + Exception e = KernelOps.CreateExceptionToRaise(respondToStorage, storage0, storage1, setBackTraceStorage, context, obj, arg, backtrace); RaiseAsyncException(self, e); #endif } @@ -654,7 +655,7 @@ trace.AppendLine(); trace.AppendLine(); RubyExceptionData data = RubyExceptionData.GetInstance(e); - if (data.Backtrace != null) { // ReinitializeException sets Backtrace to null + if (data.Backtrace != null) { foreach (var frame in data.Backtrace) { trace.Append(frame.ToString()); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C761655 File: RubyClass.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;C761655 (server) 3/2/2009 1:02 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Builtins/RubyClass.cs;exc @@ -864,7 +864,7 @@ argsBuilder.AddCallArguments(metaBuilder, args); if (!metaBuilder.Error) { - metaBuilder.Result = MakeAllocatorCall(args, () => AstUtils.Constant(Name)); + metaBuilder.Result = MakeAllocatorCall(args, AstUtils.Constant(Name)); } } @@ -894,7 +894,7 @@ // Is user class (defined in Ruby code) => construct it as if it had initializer that calls super immediately // (we need to "inherit" factories/constructors from the base class (e.g. class S < String; self; end.new('foo')). if (overriddenInitializer != null || (_isRubyClass && _structInfo == null)) { - metaBuilder.Result = MakeAllocatorCall(args, () => AstUtils.Constant(Name)); + metaBuilder.Result = MakeAllocatorCall(args, AstUtils.Constant(Name)); if (overriddenInitializer != null || (_isRubyClass && initializer != null && !initializer.IsEmpty)) { BuildOverriddenInitializerCall(metaBuilder, args, initializer); @@ -964,11 +964,15 @@ } } - public Expression/*!*/ MakeAllocatorCall(CallArguments/*!*/ args, Func/*!*/ defaultExceptionMessage) { + public Expression/*!*/ MakeAllocatorCall(CallArguments/*!*/ args, Expression/*!*/ clrExceptionMessage) { + return MakeAllocatorCall(args, args.TargetExpression, clrExceptionMessage); + } + + public Expression/*!*/ MakeAllocatorCall(CallArguments/*!*/ args, Expression/*!*/ classExpression, Expression/*!*/ clrExceptionMessage) { Type type = GetUnderlyingSystemType(); if (_structInfo != null) { - return Methods.AllocateStructInstance.OpCall(AstUtils.Convert(args.TargetExpression, typeof(RubyClass))); + return Methods.AllocateStructInstance.OpCall(AstUtils.Convert(classExpression, typeof(RubyClass))); } if (type.IsSubclassOf(typeof(Delegate))) { @@ -978,14 +982,14 @@ ConstructorInfo ctor; if (IsException()) { if ((ctor = type.GetConstructor(new[] { typeof(string) })) != null) { - return Ast.New(ctor, defaultExceptionMessage()); + return Ast.New(ctor, clrExceptionMessage); } else if ((ctor = type.GetConstructor(new[] { typeof(string), typeof(Exception) })) != null) { - return Ast.New(ctor, defaultExceptionMessage(), AstUtils.Constant(null)); + return Ast.New(ctor, clrExceptionMessage, AstUtils.Constant(null)); } } if ((ctor = type.GetConstructor(new[] { typeof(RubyClass) })) != null) { - return Ast.New(ctor, AstUtils.Convert(args.TargetExpression, typeof(RubyClass))); + return Ast.New(ctor, AstUtils.Convert(classExpression, typeof(RubyClass))); } if ((ctor = type.GetConstructor(new[] { typeof(RubyContext) })) != null) { =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Hosting/RubyOptionsParser.cs;C748168 File: RubyOptionsParser.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Hosting/RubyOptionsParser.cs;C748168 (server) 3/2/2009 1:23 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Hosting/RubyOptionsParser.cs;exc @@ -83,6 +83,8 @@ } switch (optionName) { + #region Ruby options + case "-v": CommonConsoleOptions.PrintVersion = true; CommonConsoleOptions.Exit = true; @@ -119,6 +121,12 @@ CommonConsoleOptions.Command += PopNextArg(); break; + case "-I": + _loadPaths.AddRange(PopNextArg().Split(Path.PathSeparator)); + break; + + #endregion + #if DEBUG && !SILVERLIGHT case "-DT*": SetTraceFilter(String.Empty, false); @@ -152,10 +160,6 @@ LanguageSetup.Options["CompileRegexps"] = true; break; #endif - case "-I": - _loadPaths.AddRange(PopNextArg().Split(Path.PathSeparator)); - break; - case "-trace": LanguageSetup.Options["EnableTracing"] = ScriptingRuntimeHelpers.True; break; @@ -204,11 +208,43 @@ base.GetHelp(out commandLine, out standardOptions, out environmentVariables, out comments); string [,] rubyOptions = new string[,] { - { "-opt", "dummy" }, + // { "-0[octal]", "specify record separator (\0, if no argument)" }, + // { "-a", "autosplit mode with -n or -p (splits $_ into $F)" }, + // { "-c", "check syntax only" }, + // { "-Cdirectory", "cd to directory, before executing your script" }, + { "-d", "set debugging flags (set $DEBUG to true)" }, + { "-e 'command'", "one line of script. Several -e's allowed. Omit [programfile]" }, + // { "-Fpattern", "split() pattern for autosplit (-a)" }, + // { "-i[extension]", "edit ARGV files in place (make backup if extension supplied)" }, + { "-Idirectory", "specify $LOAD_PATH directory (may be used more than once)" }, + // { "-Kkcode", "specifies KANJI (Japanese) code-set" }, + // { "-l", "enable line ending processing" }, + // { "-n", "assume 'while gets(); ... end' loop around your script" }, + // { "-p", "assume loop like -n but print line also like sed" }, + { "-rlibrary", "require the library, before executing your script" }, + // { "-s", "enable some switch parsing for switches after script name" }, + // { "-S", "look for the script using PATH environment variable" }, + // { "-T[level]", "turn on tainting checks" }, + { "-v", "print version number, then turn on verbose mode" }, + { "-w", "turn warnings on for your script" }, + { "-W[level]", "set warning level; 0=silence, 1=medium, 2=verbose (default)" }, + // { "-x[directory]", "strip off text before #!ruby line and perhaps cd to directory" }, #if DEBUG + { "-opt", "dummy" }, + { "-DT", "" }, + { "-DT*", "" }, + { "-ET", "" }, + { "-ET*", "" }, + { "-save [path]", "Save generated code to given path" }, + { "-load", "Load pre-compiled code" }, { "-useThreadAbortForSyncRaise", "For testing purposes" }, { "-compileRegexps", "Faster throughput, slower startup" }, #endif + { "-trace", "Enable support for set_trace_func" }, + { "-profile", "Enable support Clr.profile" }, + { "-18", "Ruby 1.8 mode" }, + { "-19", "Ruby 1.9 mode" }, + { "-20", "Ruby 2.0 mode" }, }; // Append the Ruby-specific options and the standard options =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C764619 File: Loader.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;C764619 (server) 3/2/2009 1:40 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/Loader.cs;exc @@ -185,7 +185,7 @@ string savePath = _context.RubyOptions.SavePath; if (savePath != null) { lock (_compiledFileMutex) { - var assemblyPath = Path.Combine(savePath, Path.GetFileName(_context.RubyOptions.MainFile) + ".dll"); + var assemblyPath = Path.Combine(savePath, (Path.GetFileName(_context.RubyOptions.MainFile) ?? "snippets") + ".dll"); Utils.Log(String.Format("SAVING to {0}", Path.GetFullPath(assemblyPath)), "LOADER"); =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C749683 File: RubyExceptionData.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C749683 (server) 3/2/2009 10:36 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;exc @@ -55,44 +55,36 @@ // can be set explicitly by the user (even to nil): private RubyArray _backtrace; + private bool _backtraceInitialized; - // false if _backtrace needs to be initialized. - private bool _backtraceInitialized; + private SetBacktraceStorage/*!*/ _setBacktraceStorage = new SetBacktraceStorage(); - // Compiled trace: contains frames above and including the first Ruby filter/catch site that the exception was caught by: - private StackTrace _catchSiteTrace; - - // Compiled trace: contains frames starting with the throw site up to the first filter/catch that the exception was caught by: - private StackTrace _throwSiteTrace; - private RubyExceptionData(Exception/*!*/ exception) { _exception = exception; _visibleException = exception; _throwingThread = Thread.CurrentThread; } - // Called lazily to create a Ruby backtrace. - private void CreateBacktrace() { + private void CreateBacktrace(RubyContext context, StackTrace catchSiteTrace) { int skipFrames = 0; bool hasFileAccessPermissions = DetectFileAccessPermissions(); var result = new RubyArray(); - if (_throwSiteTrace == null) { - SetCompiledTrace(); - } + // Compiled trace: contains frames starting with the throw site up to the first filter/catch that the exception was caught by: + StackTrace throwSiteTrace = DebugInfoAvailable ? new StackTrace(_exception, true) : new StackTrace(_exception); + AddBacktrace(result, throwSiteTrace.GetFrames(), hasFileAccessPermissions, skipFrames, false); - AddBacktrace(result, _throwSiteTrace.GetFrames(), hasFileAccessPermissions, skipFrames, false); - - if (_catchSiteTrace != null) { + if (catchSiteTrace != null) { // skip one frame - the catch-site frame is already included - AddBacktrace(result, _catchSiteTrace.GetFrames(), hasFileAccessPermissions, 1, false); + AddBacktrace(result, catchSiteTrace.GetFrames(), hasFileAccessPermissions, 1, false); } _backtrace = result; _backtraceInitialized = true; + SetBacktrace(_setBacktraceStorage, context, _backtrace); } - internal void SetCompiledTrace() { + internal void SetCompiledTrace(RubyContext/*!*/ context) { if (_exception != _visibleException) { // Thread#raise uses Thread.Abort to raise an async exception. In such cases, a different instance of // ThreadAbortException is thrown at the end of every catch block (as long as Thread.ResetAbort is not called). @@ -105,8 +97,10 @@ Debug.Assert(!_backtraceInitialized); - _catchSiteTrace = DebugInfoAvailable ? new StackTrace(true) : new StackTrace(); - _throwSiteTrace = DebugInfoAvailable ? new StackTrace(_exception, true) : new StackTrace(_exception); + // Compiled trace: contains frames above and including the first Ruby filter/catch site that the exception was caught by: + StackTrace catchSiteTrace = DebugInfoAvailable ? new StackTrace(true) : new StackTrace(); + + CreateBacktrace(context, catchSiteTrace); } internal void SetInterpretedTrace(InterpreterState/*!*/ state) { @@ -124,7 +118,21 @@ // we need to copy the trace since the source locations in frames above catch site could be altered by further interpretation: _backtrace = AddBacktrace(new RubyArray(), state, 0); + SetBacktrace(_setBacktraceStorage, state.ScriptCode.LanguageContext as RubyContext, _backtrace); + } + + public RubyArray SetBacktrace(SetBacktraceStorage setBacktraceStorage, RubyContext context, RubyArray backtrace) { _backtraceInitialized = true; + + if (context == null) { + // TODO: If we do not have a RubyContext, we cannot dispatch to the overriden Exception#set_backtrace method. + // We need to ensure that we have a non-null RubyContext. + Backtrace = backtrace; + return backtrace; + } else { + var site = setBacktraceStorage.GetCallSite(); + return site.Target(site, context, _exception, backtrace); + } } internal static RubyArray/*!*/ CreateBacktrace(RubyContext/*!*/ context, IEnumerable/*!*/ stackTrace, int skipFrames) { @@ -362,8 +370,13 @@ public RubyArray Backtrace { get { if (!_backtraceInitialized) { - CreateBacktrace(); + // This path is rarely executed - if $! is accessed from a ensure clause without a rescue clause. + // Ast.Body.TransformExceptionHandling should be updated such that SetCompiledTrace is always called, + // which will guarantee that the backtrace is created after every exception before any Ruby code + // is executed. + CreateBacktrace(null, null); } + return _backtrace; } set { @@ -372,20 +385,6 @@ } } - /// - /// Called from Kernel#raise, when throwing a user created Exception objects - /// Clears out any backtrace set by the user - /// This causes the new one to be lazily created the next time it is accessed - /// - public static void ClearBacktrace(Exception e) { - RubyExceptionData result = TryGetInstance(e); - if (result != null) { - result._backtraceInitialized = false; - result._backtrace = null; - result._catchSiteTrace = null; - } - } - public static string/*!*/ GetClrMessage(object message, string/*!*/ className) { // TODO: we can use to_s protocol conversion that doesn't throw an exception: var str = message as MutableString; =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C761655 File: RubyOps.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;C761655 (server) 3/2/2009 2:18 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyOps.cs;exc @@ -1303,7 +1303,7 @@ [Emitted] //Body: public static void SetCurrentExceptionAndStackTrace(RubyScope/*!*/ scope, Exception/*!*/ exception) { if (RubyExceptionData.TryGetInstance(exception) == null) { - RubyExceptionData.AssociateInstance(exception).SetCompiledTrace(); + RubyExceptionData.AssociateInstance(exception).SetCompiledTrace(scope.RubyContext); } scope.RubyContext.CurrentException = exception; } @@ -1357,6 +1357,15 @@ } [Emitted] + public static Exception/*!*/ InitializeException(Exception/*!*/ e, object message, bool captureStackTrace) { + RubyExceptionData.InitializeException(e, message); + if (captureStackTrace == false) { + RubyExceptionData.GetInstance(e).Backtrace = null; + } + return e; + } + + [Emitted] public static ArgumentException/*!*/ CreateArgumentsError(string message) { return (ArgumentException)RubyExceptions.CreateArgumentError(message); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C747909 File: RubyUtils.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C747909 (server) 3/2/2009 10:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;exc @@ -70,6 +70,11 @@ } } + public class SetBacktraceStorage : CallSiteStorage> { + public CallSite>/*!*/ GetCallSite() { + return GetCallSite("set_backtrace", 1); + } + } public static class RubyUtils { #region Objects =================================================================== add: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/RubySpec/RubySpec.csproj File: RubySpec.csproj =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/RubySpec/RubySpec.csproj;exc @@ -1,0 +1,58 @@ +? + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {534A82C6-3EB0-4888-ABD2-A15284683E8D} + Exe + Properties + RubySpec + RubySpec + v3.5 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + IronRuby-tags\critical_tags.txt + + + + + MSpec\Spec\spec_helper.rb + + + RubySpec\spec_helper.rb + + + \ No newline at end of file =================================================================== add: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/RubySpec/RubySpec.csproj.vspscc File: RubySpec.csproj.vspscc =================================================================== --- [no source file] +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Tests/RubySpec/RubySpec.csproj.vspscc;exc @@ -1,0 +1,10 @@ +?"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} =================================================================== add: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/core/exception/backtrace_tags.txt File: backtrace_tags.txt =================================================================== --- [no source file] +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/core/exception/backtrace_tags.txt;exc @@ -1,0 +1,1 @@ +fails:Exception#backtrace is set for exceptions in an ensure block =================================================================== delete: $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/library/uri/parse_tags.txt;C715722 File: parse_tags.txt =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/ironruby-tags/library/uri/parse_tags.txt;C715722 (server) 2/26/2009 4:41 PM +++ [no target file] @@ -1,1 +1,0 @@ -fails:URI.parse populates the components of a parsed URI::FTP object =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/backtrace_spec.rb;C715722 File: backtrace_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/backtrace_spec.rb;C715722 (server) 3/3/2009 12:48 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/backtrace_spec.rb;exc @@ -1,1 +1,10 @@ require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/fixtures/common' + +describe "Exception#backtrace" do + it "is set for exceptions in an ensure block" do + # IronRuby used to get this wrong. + ExceptionSpecs.record_exception_from_ensure_block_with_rescue_clauses + /record_exception_from_ensure_block_with_rescue_clauses/.match(ScratchPad.recorded.join).should_not be_nil + end +end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/exception_spec.rb;C715722 File: exception_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/exception_spec.rb;C715722 (server) 3/1/2009 11:51 PM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/exception_spec.rb;exc @@ -1,10 +1,82 @@ require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/../../fixtures/class' +require File.dirname(__FILE__) + '/fixtures/common' require File.dirname(__FILE__) + '/shared/new' describe "Exception.exception" do it_behaves_like(:exception_new, :exception) end +describe "Exception.exception" do + it "returns new exception even if argument is Exception (unlike Exception#exception)" do + e = Exception.new + Exception.exception(e).message.should equal(e) + end + + it "returns new exception with string message" do + Exception.exception("new message").message.should == "new message" + end + + it "creates new object if an argument responding to to_s is given" do + m = ClassSpecs::A.new + m.should_receive(:to_s).any_number_of_times.and_return("new message") + e2 = Exception.exception(m) + e2.message.should equal(m) + end + + it "allows nil argument" do + e2 = Exception.exception(nil) + e2.message.should == "Exception" + end +end + +describe "Exception#exception" do + before(:each) do + @e = RuntimeError.new "test message" + @e.set_backtrace ["func0", "func1"] + end + + it "returns self if no arguments are given" do + @e.exception.should equal(@e) + end + + it "returns self if argument is self" do + @e.exception(@e).should equal(@e) + end + + it "preserves backtrace" do + b = @e.backtrace + @e.exception.backtrace.should equal(b) + end + + it "creates new object ands sets #message to argument" do + ["new message", ClassSpecs::A.new, ClassSpecs::Undef_to_s.new].each do |m| + e2 = @e.exception(m) + e2.message.should equal(m) + end + end + + it "does not call initialize" do + e = ExceptionSpecs::InitializedException.new + ScratchPad.clear + e2 = e.exception("new message") + + e2.should_not equal(e) + ScratchPad.recorded.should == nil + end + + it "creates a new object with an empty backtrace" do + e2 = @e.exception("new message") + e2.backtrace.should be_nil + end + + it "allows nil argument" do + e2 = @e.exception(nil) + e2.should_not equal(@e) + e2.message.should == @e.class.to_s + end +end + describe "Exception" do it "is a Class" do Exception.should be_kind_of(Class) =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/fixtures/common.rb;C715722 File: common.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/fixtures/common.rb;C715722 (server) 3/2/2009 12:31 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/core/exception/fixtures/common.rb;exc @@ -12,3 +12,26 @@ class NoMethodErrorD; end end + +module ExceptionSpecs + class InitializedException < Exception + def initialize + ScratchPad.record :initialized_exception + end + end + + def self.exception_from_ensure_block_with_rescue_clauses + begin + raise "some message" + ensure + ScratchPad.record $!.backtrace + end + end + + def self.record_exception_from_ensure_block_with_rescue_clauses + begin + exception_from_ensure_block_with_rescue_clauses + rescue + end + end +end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/fixtures/class.rb;C715722 File: class.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/fixtures/class.rb;C715722 (server) 3/2/2009 12:27 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/fixtures/class.rb;exc @@ -85,6 +85,16 @@ class L; end class M < L; end + + class Undef_to_s + undef to_s + end + + class InitializeMethod + def initialize + ScratchPad.record :initialize_method + end + end end class Class =================================================================== add: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/fixtures/kernel/classes.rb File: classes.rb =================================================================== --- [no source file] +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/fixtures/kernel/classes.rb;exc @@ -1,0 +1,29 @@ +module KernelSpecs + class TestException < StandardError + def initialize record_exception_method = false, record_set_backtrace = false + @record_exception_method = record_exception_method + @record_set_backtrace = record_set_backtrace + end + + def exception arg="default value" + if @record_exception_method then + ScratchPad << self + ScratchPad << :exception_method + ScratchPad << arg + end + + TestException.new(@record_exception_method, @record_set_backtrace) + end + + def set_backtrace b + if @record_set_backtrace then + ScratchPad << self + ScratchPad << :set_backtrace_method + ScratchPad << b.class + ScratchPad << b[0].class + end + super(b) + end + end + +end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/regexp_spec.rb;C754096 File: regexp_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/regexp_spec.rb;C754096 (server) 3/2/2009 9:45 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/regexp_spec.rb;exc @@ -1,5 +1,6 @@ require File.dirname(__FILE__) + '/../spec_helper' require File.dirname(__FILE__) + '/fixtures/classes' +require File.dirname(__FILE__) + '/../fixtures/class' describe "Literal Regexps" do it "matches against $_ (last input) in a conditional if no explicit matchee provided" do @@ -95,7 +96,7 @@ end it "throws NoMethodError on missing to_s" do - o = LanguageSpecs::ClassWithout_to_s.new + o = ClassSpecs::Undef_to_s.new lambda { /#{o}/ }.should raise_error(NoMethodError) end =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/fixtures/classes.rb;C754096 File: classes.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/fixtures/classes.rb;C754096 (server) 3/2/2009 1:12 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/language/fixtures/classes.rb;exc @@ -13,10 +13,6 @@ end end - class ClassWithout_to_s - undef :to_s - end - ############################################################################# # Regexp support ############################################################################# =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/library/uri/parse_spec.rb;C715722 File: parse_spec.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/library/uri/parse_spec.rb;C715722 (server) 2/27/2009 9:55 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/library/uri/parse_spec.rb;exc @@ -54,7 +54,8 @@ URI.parse("https://example.com/").port.should == 443 end - ruby_version_is "".."1.8.6" do + compliant_on :ruby do + ruby_version_is "".."1.8.5" do it "populates the components of a parsed URI::FTP object" do # generic, empty password. url = URI.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") @@ -95,8 +96,9 @@ end end + end - ruby_version_is "1.8.7".."" do + ruby_bug "", "1.8.5" do it "populates the components of a parsed URI::FTP object" do # generic, empty password. url = URI.parse("ftp://anonymous@ruby-lang.org/pub/ruby/1.8/ruby-1.8.6.tar.bz2;type=i") =================================================================== edit: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/shared/kernel/raise.rb;C715722 File: raise.rb =================================================================== --- $/Merlin_External/Languages/IronRuby/mspec/rubyspec/shared/kernel/raise.rb;C715722 (server) 3/2/2009 9:39 AM +++ Shelved Change: $/Merlin_External/Languages/IronRuby/mspec/rubyspec/shared/kernel/raise.rb;exc @@ -1,3 +1,5 @@ +require File.dirname(__FILE__) + '/../../fixtures/kernel/classes' + describe :kernel_raise, :shared => true do before :each do ScratchPad.clear @@ -36,8 +38,55 @@ end it "allows Exception, message, and backtrace parameters" do - lambda do - @object.raise(ArgumentError, "message", caller) - end.should raise_error(ArgumentError, "message") + b = ["func0", "func1"] + begin + @object.raise(ArgumentError, "test message", b) + rescue ArgumentError => e + end + e.backtrace.should == b + e.message == "test message" end + + it "calls Exception#exception when raising an existing Exception object" do + ScratchPad.record [] + existing = KernelSpecs::TestException.new true + begin + raise existing + rescue => e + ScratchPad << e + end + ScratchPad.recorded[0].should equal(existing) + ScratchPad.recorded[1..2].should == [:exception_method, "default value"] + ScratchPad.recorded[3].should_not equal(existing) + end + + it "calls Exception#set_backtrace when raising an existing Exception object" do + ScratchPad.record [] + existing = KernelSpecs::TestException.new false, true + begin + raise existing + rescue => e + ScratchPad << e + end + ScratchPad.recorded[0].should_not equal(existing) + ScratchPad.recorded[1..3].should == [:set_backtrace_method, Array, String] + ScratchPad.recorded[0].should equal(ScratchPad.recorded[4]) + end + + it "accepts any object responding to #exception" do + m = mock("non-Exception") + m.should_receive(:exception).and_return(KernelSpecs::TestException.new) + lambda { raise m }.should raise_error(KernelSpecs::TestException) + end + + it "requires #exception to return an Exception" do + m = mock("non-Exception") + m.should_receive(:exception).and_return(m) + lambda { raise m }.should raise_error(TypeError) + end + + it "requires an object responding to #exception" do + m = mock("non-Exception") + lambda { raise m }.should raise_error(TypeError) + end end ===================================================================