/// <summary> /// Creates a new PHP stack frame. /// </summary> /// <param name="context">A script context.</param> /// <param name="frame">The respective CLR frame.</param> /// <param name="kind">A kind of the frame.</param> internal PhpStackFrame(ScriptContext /*!*/ context, StackFrame /*!*/ frame, FrameKinds kind) { Debug.Assert(context != null && frame != null && kind != FrameKinds.Invisible); this.frame = frame; this.kind = kind; MethodBase method = frame.GetMethod(); if (kind == FrameKinds.ClassLibraryFunction) { this.name = ImplementsFunctionAttribute.Reflect(method).Name; SetDebugInfo(frame); } else { Type type = method.DeclaringType; int eval_id = TransientAssembly.InvalidEvalId; if (type != null && context.ApplicationContext.IsTransientRealType(type)) { // gets [PhpEvalId] attribute defined on the type: object[] attrs = type.GetCustomAttributes(typeof(PhpEvalIdAttribute), false); eval_id = ((PhpEvalIdAttribute)attrs[0]).Id; ErrorStackInfo info = new ErrorStackInfo(); PhpStackTrace.FillEvalStackInfo(context, eval_id, ref info, false); this.line = info.Line; this.column = info.Column; this.file = info.File; this.name = info.Caller; } else { SetDebugInfo(frame); } // the caller has already been set by FillEvalStackInfo // if it is not an eval main: if (!(eval_id != TransientAssembly.InvalidEvalId && PhpScript.IsScriptType(type) && kind == FrameKinds.Main)) { if (this.name == ScriptModule.MainHelperName) { this.name = "{main}"; } else { int j; PhpScript.ParseMDeclName(method.Name, out this.name, out j); } } } }
/// <summary> /// Traces up the stack frame containing the method call that has caused an error. /// </summary> /// <returns>Found stack info.</returns> /// <remarks> /// Starts with a frame of a calling method and ends with the first frame belonging to user routine. /// If there was <see cref="ImplementsFunctionAttribute"/> found during the walk the last one's value /// is considered as the caller. /// If there was not such attribute found (error occured in an operator, directly in the code etc.) /// the last inspected method's debug info is returned. /// If the trace ends up with a function or method inside transient assembly an eval hierarchy is inspected /// and added to the resulting source position information. /// </remarks> internal static ErrorStackInfo TraceErrorFrame(ScriptContext /*!*/ context, bool lazy) { Debug.Assert(context != null); ErrorStackInfo result = new ErrorStackInfo(); int cl_function_idx = -1; string cl_function_name = null; StackFrame frame; int eval_id = TransientAssembly.InvalidEvalId; // stack trace without debug info is constructed: #if !SILVERLIGHT StackTrace trace = new StackTrace(1, false); // note: method stack frame contains a debug info about the call to the callee // hence if we find a method that reported the error we should look the next frame // to obtain a debug info int i = 0; for (; ;) { // gets frame: frame = trace.GetFrame(i++); // error has been thrown directly by Core without intermediary user code (all frames are invisible): if (frame == null) { // cl_function_idx can be non-minus-one here because a callback can be called directly from Core // (e.g. output buffer filter targeting class library function): if (cl_function_idx != -1) { result.Caller = cl_function_name; result.LibraryCaller = true; } return(result); } MethodBase method = frame.GetMethod(); if (lazy) { if (method.Name == "Throw" && method.DeclaringType == typeof(PhpException)) { lazy = false; } continue; } FrameKinds frame_kind = GetFrameKind(frame); if (frame_kind == FrameKinds.Visible || frame_kind == FrameKinds.Main) { int eid = TransientModule.GetEvalId(context.ApplicationContext, method); if (eval_id == TransientAssembly.InvalidEvalId) { eval_id = eid; } if (eid == TransientAssembly.InvalidEvalId) { break; } } else if (frame_kind == FrameKinds.ClassLibraryFunction) { cl_function_idx = i; cl_function_name = ImplementsFunctionAttribute.Reflect(method).Name; } } // skips i frames (the very first frame has been skipped in the previous // trace construction and we want to skip i-1 frames from that trace => i frames totally): frame = new StackFrame(1 + i - 1, true); // extracts a source info (file & position): if (eval_id != TransientAssembly.InvalidEvalId) { FillEvalStackInfo(context, eval_id, ref result, false); } else { result.Line = frame.GetFileLineNumber(); result.Column = frame.GetFileColumnNumber(); result.File = frame.GetFileName(); } // determines a caller (either a library function or a user function/method): if (cl_function_idx >= 0) { result.Caller = cl_function_name; result.LibraryCaller = true; } //else //{ // MethodBase method = frame.GetMethod(); // Type type = method.DeclaringType; // // the caller has already been set by FillEvalStackInfo // // if we are in eval and the function is Main helper of the script type: // if (eval_id == TransientAssembly.InvalidEvalId) // { // result.LibraryCaller = false; // if (type != null) // { // result.Caller = String.Concat(DTypeDesc.MakeFullName(type), "::", DRoutineDesc.MakeFullName(method)); // } // else // { // result.Caller = DRoutineDesc.MakeFullName(method); // } // } //} #endif // add missing info about file and line context.LastErrorLine = result.Line; context.LastErrorFile = result.File; // return(result); }