edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Console/Program.cs;C402163 File: Program.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Console/Program.cs;C402163 (server) 5/21/2008 1:50 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Console/Program.cs;Caller1 @@ -26,6 +26,7 @@ using Microsoft.Scripting.Generation; using Microsoft.Scripting.Hosting.Shell; using Ruby.Runtime; +using Ruby; internal sealed class RubyConsoleHost : ConsoleHost { @@ -42,6 +43,7 @@ } [STAThread] + [RubyStackTraceHidden] static int Main(string[] args) { return new RubyConsoleHost().Run(args); } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C445173 File: Initializers.Generated.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;C445173 (server) 5/21/2008 11:02 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Initializers.Generated.cs;Caller1 @@ -49,9 +49,9 @@ #endif // Skipped primitive: Object ExtendModule(typeof(System.Collections.Generic.IDictionary), new System.Action(LoadSystem__Collections__Generic__IDictionary_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); - ExtendModule(typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); + Ruby.Builtins.RubyModule def29 = ExtendModule(typeof(System.Collections.IEnumerable), new System.Action(LoadSystem__Collections__IEnumerable_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); ExtendModule(typeof(System.Collections.IList), new System.Action(LoadSystem__Collections__IList_Instance), null, new Ruby.Builtins.RubyModule[] {def16, }); - Ruby.Builtins.RubyModule def29 = ExtendModule(typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new Ruby.Builtins.RubyModule[] {def25, }); + ExtendModule(typeof(System.IComparable), new System.Action(LoadSystem__IComparable_Instance), null, new Ruby.Builtins.RubyModule[] {def25, }); DefineGlobalClass("Time", typeof(System.DateTime), classRef0, new System.Action(LoadTime_Instance), new System.Action(LoadTime_Class), new Ruby.Builtins.RubyModule[] {def25, }, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.TimeOps.Create), }); @@ -2202,6 +2202,10 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.HasBlock), }); + module.DefineLibraryMethod("caller", 0x2a, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.GetStackTrace), + }); + module.DefineLibraryMethod("class", 0x29, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.GetClass), }); @@ -2466,6 +2470,10 @@ new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.AtExit), }); + module.DefineLibraryMethod("caller", 0x31, new System.Delegate[] { + new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.GetStackTrace), + }); + module.DefineLibraryMethod("eval", 0x31, new System.Delegate[] { new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), new Microsoft.Scripting.Utils.Function(Ruby.Builtins.Kernel.Evaluate), =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C445173 File: Kernel.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;C445173 (server) 5/21/2008 10:40 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Kernel.cs;Caller1 @@ -25,6 +25,7 @@ using Microsoft.Scripting.Utils; using Ruby.Runtime; using Ruby.Runtime.Calls; +using System.Diagnostics; namespace Ruby.Builtins { @@ -33,15 +34,12 @@ #region Private Instance Methods - //active_gem_with_options - //gem_original_require //initialize_copy - //location_of_caller //remove_instance_variable + //singleton_method_added //singleton_method_removed //singleton_method_undefined - //y #endregion @@ -111,7 +109,23 @@ } //callcc - //caller + + [RubyMethod("caller", RubyMethodAttributes.PrivateInstance)] + [RubyMethod("caller", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] + public static RubyArray/*!*/ GetStackTrace(object self, [DefaultParameterValue(1)]int skipFrames) { + if (skipFrames < 0) { + return new RubyArray(); + } + +#if SILVERLIGHT + StackTrace trace = new StackTrace(); +#else + StackTrace trace = new StackTrace(true); +#endif + return RubyExceptionData.CreateBacktrace(trace.GetFrames(), skipFrames); + } + //catch //chomp //chomp! @@ -295,6 +309,7 @@ [RubyMethod("method_missing", RubyMethodAttributes.PrivateInstance)] [RubyMethod("method_missing", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] public static object MethodMissing(CodeContext/*!*/ context, object/*!*/ self, BlockParam block, SymbolId name, [NotNull]params object[]/*!*/ args) { throw RubyExceptions.CreateMethodMissing(context, self, name); } @@ -399,6 +414,7 @@ [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] public static void RaiseException(CodeContext/*!*/ context, object self) { RubyExecutionContext ec = RubyUtils.GetExecutionContext(context); @@ -415,6 +431,7 @@ [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] public static void RaiseException(CodeContext/*!*/ context, object self, [NotNull]MutableString/*!*/ message) { ContractUtils.RequiresNotNull(message, "message"); RubyExecutionContext ec = RubyUtils.GetExecutionContext(context); @@ -425,6 +442,7 @@ [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] public static void RaiseException(CodeContext/*!*/ context, object self, [NotNull]RubyClass/*!*/ exceptionClass, [Optional]MutableString message, [Optional]RubyArray stackTrace) { RubyExecutionContext ec = RubyUtils.GetExecutionContext(context); Exception e = ExceptionOps.MakeException(context, exceptionClass, message, stackTrace); @@ -436,6 +454,7 @@ [RubyMethod("raise", RubyMethodAttributes.PublicSingleton)] [RubyMethod("fail", RubyMethodAttributes.PrivateInstance)] [RubyMethod("fail", RubyMethodAttributes.PublicSingleton)] + [RubyStackTraceHidden] public static void RaiseException(CodeContext/*!*/ context, object self, object/*!*/ exceptionObject, [Optional]MutableString message, [Optional]List stackTrace) { // exceptionObject <# { exception: void -> Exception } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Attributes.cs;C444795 File: Attributes.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Attributes.cs;C444795 (server) 5/21/2008 1:39 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Attributes.cs;Caller1 @@ -292,5 +292,9 @@ _name = name; } } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public sealed class RubyStackTraceHiddenAttribute : Attribute { + } } =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/SourceUnitTree.cs;C438696 File: SourceUnitTree.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/SourceUnitTree.cs;C438696 (server) 5/21/2008 2:00 PM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/SourceUnitTree.cs;Caller1 @@ -55,7 +55,7 @@ internal MSA.LambdaExpression/*!*/ Transform(AstGenerator/*!*/ gen, SourceCodeKind kind) { Debug.Assert(gen != null); - MSA.LambdaBuilder method = AstUtils.Lambda(typeof(object), "main", Location); + MSA.LambdaBuilder method = AstUtils.Lambda(typeof(object), RubyExceptionData.TopLevelMethodName, Location); method.Dictionary = true; MSA.Expression rfcVariable, selfVariable, runtimeScopeVariable; =================================================================== edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Compiler/Ast/Declarations/MethodDeclaration.cs;C444795 edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C435539 File: RubyExceptionData.cs =================================================================== --- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;C435539 (server) 5/21/2008 10:52 AM +++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyExceptionData.cs;Caller1 @@ -20,6 +20,8 @@ using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; using Ruby.Builtins; +using System.Diagnostics; +using System.Collections.Generic; namespace Ruby.Runtime { /// @@ -28,6 +30,7 @@ [Serializable] public class RubyExceptionData { private static readonly object/*!*/ _DataKey = new object(); + internal const string TopLevelMethodName = "#top-level-method#"; private Exception/*!*/ _exception; // owner exception, needed for lazy initialization of message, backtrace private MutableString _message; // if this is set to null we need to initialze it @@ -38,34 +41,127 @@ _exception = exception; } - // TODO: better filtering - private RubyArray/*!*/ CreateBacktrace() { + public static RubyArray/*!*/ CreateBacktrace(Exception/*!*/ exception) { +#if SILVERLIGHT // TODO: StackTrace.ctor(exception) security critical RubyArray result = new RubyArray(); - foreach (DynamicStackFrame frame in ExceptionHelpers.GetStackFrames(_exception)) { - MethodBase method = frame.GetMethod(); - Type type = method.DeclaringType; - // skip frames in IronRuby.dll - if (type != null && type.Assembly == GetType().Assembly) { + foreach (string line in exception.StackTrace.Split('\n')) { + string frame = line.Trim(); + if (frame.StartsWith("at ")) { + frame = frame.Substring("at ".Length); + } + + if (frame.StartsWith("_stub_") || + frame.StartsWith("Microsoft.Scripting") || + frame.StartsWith("System.Runtime") || + frame.StartsWith("Ruby.Builtins.Kernel.RaiseException") || + frame.StartsWith("Ruby.Builtins.Kernel.MethodMissing")) { continue; } - - // skip Kernel#raise and Kernel#method_missing - if (type != null && type.FullName == "Ruby.Builtins.Kernel" && (method.Name == "RaiseException" || method.Name == "MethodMissing") - || method.Name.StartsWith("Ruby.Builtins.Kernel.RaiseException") - || method.Name.StartsWith("Ruby.Builtins.Kernel.MethodMissing")) { - continue; + + int dollar = frame.IndexOf('$'); + if (dollar != -1) { + frame = frame.Substring(0, dollar); } - result.Add(MutableString.Create(string.Format( - "{0}:{1}:in `{2}'", - frame.GetFileName(), - frame.GetFileLineNumber(), - frame.GetMethodName()))); + result.Add(FormatFrame("", 0, frame)); } + + return result; +#else + return CreateBacktrace(new StackTrace(exception, true).GetFrames(), 0); +#endif + } + public static RubyArray/*!*/ CreateBacktrace(StackFrame[]/*!*/ stackTrace, int skipFrames) { + RubyArray result = new RubyArray(); + foreach (StackFrame frame in stackTrace) { + if (IsVisibleFrame(frame.GetMethod())) { + if (skipFrames == 0) { + string methodName, file; + int line; + GetStackFrameInfo(frame, out methodName, out file, out line); + result.Add(MutableString.Create(FormatFrame(file, line, methodName))); + } else { + skipFrames--; + } + } + } + return result; } + private static string/*!*/ FormatFrame(string/*!*/ file, int line, string methodName) { + if (String.IsNullOrEmpty(methodName)) { + return String.Format("{0}:{1}", file, line); + } else { + return String.Format("{0}:{1}:in `{2}'", file, line, methodName); + } + } + + private static void GetStackFrameInfo(StackFrame/*!*/ frame, out string/*!*/ methodName, out string/*!*/ fileName, out int line) { + MethodBase method = frame.GetMethod(); + methodName = method.Name; + fileName = frame.GetFileName(); + line = frame.GetFileLineNumber(); + + // TODO: no support in DLR + int dollar = method.Name.IndexOf('$'); + if (dollar != -1) { + methodName = methodName.Substring(0, dollar); + } else { + object[] attrs = method.GetCustomAttributes(typeof(RubyMethodAttribute), false); + if (attrs.Length > 0) { + // TODO: aliases + methodName = ((RubyMethodAttribute)attrs[0]).Name; + } + } + + if (methodName == TopLevelMethodName) { + methodName = null; + } + + if (String.IsNullOrEmpty(fileName)) { + if (method.DeclaringType != null) { + fileName = method.DeclaringType.Assembly.GetName().Name; + line = 0; + } + } + } + + // TODO: better filtering in DLR + private static bool IsVisibleFrame(MethodBase/*!*/ method) { + // filter out the _stub_ methods + if (method.Name.StartsWith("_stub_")) { + return false; + } + + Type type = method.DeclaringType; + + if (type != null) { + string typeName = type.FullName; + if (typeName.StartsWith("System.Reflection.") || + typeName.StartsWith("System.Runtime") || + typeName.StartsWith("Microsoft.Scripting")) { + return false; + } + + // TODO: check loaded assemblies + if (type.Assembly == typeof(RubyOps).Assembly) { + return false; + } + + if (method.IsDefined(typeof(RubyStackTraceHiddenAttribute), false)) { + return false; + } + + if (type.Assembly.IsDefined(typeof(RubyLibraryAttribute), false)) { + return method.IsDefined(typeof(RubyMethodAttribute), false); + } + } + + return true; + } + /// /// Gets the instance data associated with the exception /// @@ -94,7 +190,7 @@ public RubyArray Backtrace { get { if (!_backtraceInitialized) { - _backtrace = CreateBacktrace(); + _backtrace = CreateBacktrace(_exception); _backtraceInitialized = true; } return _backtrace; ===================================================================