/// <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); }
internal TransientModuleBuilder(int id, EvalKinds kind, TransientCompilationUnit /*!*/ compilationUnit, TransientAssemblyBuilder /*!*/ assemblyBuilder, TransientModule containingModule, string sourcePath) : base(id, kind, compilationUnit, assemblyBuilder.TransientAssembly, containingModule, sourcePath) { this.assemblyBuilder = assemblyBuilder; if (!compilationUnit.IsDynamic) { this.globalBuilder = assemblyBuilder.RealModuleBuilder.DefineType(MakeName("<Global>", true), TypeAttributes.SpecialName | TypeAttributes.Class | TypeAttributes.Public); } else { this.globalBuilder = null; } }
/// <summary> /// Finds out a kind of a CLI frame from the PHP point of view. /// </summary> /// <param name="frame">The CLI frame.</param> /// <returns>The kind of the frame.</returns> private static FrameKinds GetFrameKind(StackFrame /*!*/ frame) { Debug.Assert(frame != null); MethodBase method_base = frame.GetMethod(); // skip CLR ctors and generic methods (we don't emit any): if (method_base.IsConstructor || method_base.IsGenericMethod) { return(FrameKinds.Invisible); } // skip various stubs (special-name) except for Main helper: if (method_base.IsSpecialName) { // main helper in PHP module (script module): if (DRoutineDesc.GetSpecialName(method_base) == ScriptModule.MainHelperName && method_base.Module.Assembly.IsDefined(typeof(ScriptAssemblyAttribute), false)) { return(FrameKinds.Main); } return(FrameKinds.Invisible); } MethodInfo method = (MethodInfo)method_base; Type type = method.DeclaringType; if (type != null) { // methods // string ns = type.Namespace; if (ns != null) { // skip Core and Extension Manger methods and Dynamic Wrapper Stubs: if (ns.StartsWith(Namespaces.Core) || ns == Namespaces.ExtManager || ns == Namespaces.LibraryStubs) { return(FrameKinds.Invisible); } // skip Class Library methods including PHP functions and PHP methods (remembering the last function): if (ns.StartsWith(Namespaces.Library)) { // find out [ImplementsFunction] attributes assigned to the method: if (method.IsDefined(Emit.Types.ImplementsFunctionAttribute, false)) { return(FrameKinds.ClassLibraryFunction); } else { return(FrameKinds.Invisible); } } return(FrameKinds.Visible); } else { // skip export stubs (and other debugger hidden functions): if (method.IsDefined(Emit.Types.DebuggerHiddenAttribute, false)) { return(FrameKinds.Invisible); } return(FrameKinds.Visible); } } else { // global functions // // skip functions of ExtSupport (and other global non-PHP functions): if (method.Module.Assembly != DynamicCode.DynamicMethodType.Assembly && !method.Module.Assembly.IsDefined(typeof(DAssemblyAttribute), false)) { return(FrameKinds.Invisible); } // transient special names: if (TransientModule.IsSpecialName(method.Name)) { // main helper is visible as it contains user code: if (DRoutineDesc.GetSpecialName(method) == ScriptModule.MainHelperName) { return(FrameKinds.Main); } return(FrameKinds.Invisible); } // skip export stubs (and other debugger hidden functions): if (method.IsDefined(Emit.Types.DebuggerHiddenAttribute, false)) { return(FrameKinds.Invisible); } // return(FrameKinds.Visible); } //// global functions (in extensions) are not included in the PHP stack trace: //if (type==null) return FrameKinds.Invisible; //string ns = type.Namespace; //// non-PHP user code: //if (ns == null) // return FrameKinds.Visible; //// Core, System, and Extension Manger methods are skipped: //if (ns.StartsWith(Namespaces.Core) || ns.StartsWith(Namespaces.System) || ns == Namespaces.ExtManager) // return FrameKinds.Invisible; //// skips library stubs: //if (ns == Namespaces.LibraryStubs) // return FrameKinds.Invisible; //// methods in user namespace (either generated by Phalanger or written in other .NET language): //if (ns.StartsWith(Namespaces.User)) //{ // // skips arg-less stubs (method is not a constructor => it has MethodInfo): // if (PhpFunctionUtils.IsArglessStub((MethodInfo)method,null)) // return FrameKinds.Invisible; // return FrameKinds.UserRoutine; //} //// skip Class Library methods including PHP functions and PHP methods (remembering the last function): //if (ns.StartsWith(Namespaces.Library)) //{ // // find out [ImplementsFunction] attributes assigned to the method: // if (method.IsDefined(Emit.Types.ImplementsFunctionAttribute,false)) // return FrameKinds.ClassLibraryFunction; else // return FrameKinds.Invisible; //} //// non-PHP user code: //return FrameKinds.Visible; }
/// <summary> /// Implements PHP <c>eval</c> construct with given code prefix and suffix. /// A result of concatanation prefix + code + suffix is compiled. /// Prefix should contain no new line characters. /// </summary> internal static object EvalInternal( string prefix, string code, string suffix, EvalKinds kind, ScriptContext /*!*/ scriptContext, Dictionary <string, object> localVariables, DObject self, DTypeDesc referringType, SourceCodeDescriptor descriptor, bool entireFile, NamingContext namingContext) { Debug.Assert(prefix != null && suffix != null); // composes code to be compiled: code = String.Concat(prefix, code, suffix); TransientAssemblyBuilder assembly_builder = scriptContext.ApplicationContext.TransientAssemblyBuilder; // looks up the cache: TransientModule module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) { // double checked lock, // if module != null, it is definitely completed // since module is added into TransientAssembly at the end // of assembly_builder.Build lock (assembly_builder.TransientAssembly) { // lookup again, since it could be added into TransientAssembly while lock module = assembly_builder.TransientAssembly.GetModule(scriptContext, referringType, code, descriptor); if (module == null) { if (kind == EvalKinds.SyntheticEval) { Debug.WriteLine("SYN EVAL", "Eval cache missed: '{0}'", code.Substring(0, Math.Max(code.IndexOf('{'), 0)).TrimEnd()); } else { Debug.WriteLine("EVAL", "Eval cache missed: '{0}'({1},{2})", descriptor.ContainingSourcePath, descriptor.Line, descriptor.Column); } CompilerConfiguration config = new CompilerConfiguration(Configuration.Application); CompilationContext context = new CompilationContext(scriptContext.ApplicationContext, null, config, new EvalErrorSink(-prefix.Length, config.Compiler.DisabledWarnings, config.Compiler.DisabledWarningNumbers), scriptContext.WorkingDirectory); TransientCompilationUnit unit = assembly_builder.Build(code, descriptor, kind, context, scriptContext, referringType, namingContext, entireFile); // compilation failed: if (unit == null) { return(false); } module = unit.TransientModule; } } } // activates unconditionally declared types, functions and constants: module.TransientCompilationUnit.Declare(scriptContext); return(module.Main(scriptContext, localVariables, self, referringType, true)); }