public PhpCallback(RoutineDelegate functionDelegate, ScriptContext context) { // create a new DRoutineDesc based on the passed delegate routineDesc = new PhpRoutineDesc(PhpMemberAttributes.Static | PhpMemberAttributes.NamespacePrivate, functionDelegate, false); this.context = context; this.state = State.Bound; }
/// <summary> /// Creates bounded PHP instance method callback. Used when we already know the routine. /// </summary> /// <param name="instance">The target PHP object.</param> /// <param name="routine">The target PHP method.</param> internal PhpCallback(DObject instance, DRoutineDesc routine) { Debug.Assert(instance != null); Debug.Assert(routine != null); this.instance = instance; this.targetName = routine.Member.FullName; this.state = State.Bound; this.routineDesc = routine; this.lateStaticBindType = instance.TypeDesc; }
/// <summary> /// Generates Expression that throws a 'Protected method called' or 'Private method called' <see cref="PhpException"/>. /// </summary> /// <param name="method">The <see cref="DRoutineDesc"/>.</param> /// <param name="callerContext">The caller that was passed to method lookup or <B>null</B> /// if it should be determined by this method (by tracing the stack).</param> /// <remarks> /// This method is intended to be called after <see cref="DTypeDesc.GetMethod"/> has returned /// <see cref="GetMemberResult.BadVisibility"/> while performing a method lookup. /// </remarks> public static Expression /*!*/ ThrowVisibilityError(DRoutineDesc /*!*/ method, DTypeDesc /*!*/ callerContext) { if (method.IsProtected) { return(ThrowError("protected_method_called", method.DeclaringType.MakeFullName(), method.MakeFullName(), callerContext == null ? String.Empty : callerContext.MakeFullName())); } else if (method.IsPrivate) { return(ThrowError("private_method_called", method.DeclaringType.MakeFullName(), method.MakeFullName(), callerContext == null ? String.Empty : callerContext.MakeFullName())); } throw new NotImplementedException(); }
public virtual object __construct(ScriptContext context, object arg) { string name = PhpVariable.AsString(arg); if (!string.IsNullOrEmpty(name)) { routine = context.ResolveFunction(name, null, false); } else { PhpException.InvalidArgument("arg"); } if (routine == null) { PhpException.Throw(PhpError.Error, string.Format("Function {0}() does not exist", name)); } return(null); }
/// <summary> /// Creates a callback bound to the specified PHP method represented by a <see cref="DRoutineDesc"/>. /// </summary> /// <param name="instance">The target PHP object.</param> /// <param name="handle">The handle of the target PHP method.</param> /// <param name="context">The script context to call the method with.</param> public PhpCallback(DObject instance, DRoutineDesc handle, ScriptContext context) { if (handle == null) { throw new ArgumentNullException("handle"); } if (!handle.IsStatic) { if (instance == null) { throw new ArgumentNullException("instance"); } this.instance = instance; } this.context = context; this.routineDesc = handle; this.state = State.Bound; if (instance != null) { this.lateStaticBindType = instance.TypeDesc; } }
public virtual object __construct(ScriptContext context, object @class, object methodname) { string methodnameStr = PhpVariable.AsString(methodname); this.dtype = null; this.method = null; DObject dobj; if ((dobj = (@class as DObject)) != null) { this.dtype = dobj.TypeDesc; } else { var str = PhpVariable.AsString(@class); if (str != null) { this.dtype = context.ResolveType(str, null, null, null, ResolveTypeFlags.UseAutoload); } if (this.dtype == null) { PhpException.Throw(PhpError.Error, string.Format("Class {0} does not exist", str)); return(false); } } if (this.dtype.GetMethod(new Name(methodnameStr), dtype, out this.method) == GetMemberResult.NotFound) { PhpException.Throw(PhpError.Error, string.Format("Method {0}::{1}() does not exist", dtype.MakeFullName(), methodnameStr)); return(false); } return(null); }
/// <summary> /// Returns delegate of T type to CLR stub of the given target-routine pair. /// </summary> /// <param name="target">The target instance or <B>null</B>.</param> /// <param name="routine">The target routine desc.</param> /// <param name="realCalleeName">Real callee name if <paramref name="routine"/> is in fact <c>__call</c>, /// or <B>null</B> if <paramref name="routine"/> if the real callee.</param> /// <returns> /// Delegate to the stub or <B>null</B> if stub for this target-routine pair cannot be generated. /// </returns> /// <remarks> /// This method is used in cases when delegate type T is known at compile-time. By caching the corresponding /// delegate type desc in a static field (see <see cref="delegateDesc"/>), repeated type desc lookups are /// completely avoided. /// </remarks> internal static T GetStub(DObject target, DRoutineDesc /*!*/ routine, string realCalleeName) { return((T)(object)delegateDesc.StubBuilder.GetStub(target, routine, realCalleeName)); }
/// <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> /// Attempts to bind this callback to its target. /// </summary> /// <param name="quiet"><B>true</B> of no errors should be thrown, <B>false</B> otherwise.</param> /// <param name="nameContext">Current <see cref="NamingContext"/> for function and class name resolution.</param> /// <param name="caller">Current class context or a <see cref="UnknownTypeDesc"/> if the class context /// should be determined ad-hoc.</param> /// <returns><B>True</B> if the callback was successfully bound, <B>false</B> if an error occured.</returns> public bool Bind(bool quiet, DTypeDesc caller, NamingContext nameContext) { if (IsInvalid) { return(false); } switch (state) { case State.UnboundFunction: { if (context == null) { context = ScriptContext.CurrentContext; } routineDesc = context.ResolveFunction(targetName, nameContext, quiet); if (routineDesc == null) { return(false); } state = State.Bound; return(true); } case State.UnboundStaticMethod: { if (context == null) { context = ScriptContext.CurrentContext; } if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // try to find the CLR method // find the class according to className ResolveTypeFlags flags = ResolveTypeFlags.UseAutoload; if (!quiet) { flags |= ResolveTypeFlags.ThrowErrors; } DTypeDesc type = context.ResolveType(className, nameContext, callingContext, null, flags); if (type == null) { return(false); } // find the method bool is_caller_method; lateStaticBindType = type; routineDesc = Operators.GetStaticMethodDesc(type, targetName, ref instance, callingContext, context, quiet, false, out is_caller_method); if (routineDesc == null) { return(false); } if (instance != null) { dummyInstance = true; } state = is_caller_method ? State.BoundToCaller : State.Bound; return(true); } case State.UnboundInstanceMethod: { if (caller != null && caller.IsUnknown) { callingContext = PhpStackTrace.GetClassContext(); } else { callingContext = caller; } // ask the instance for a handle to the method bool is_caller_method; routineDesc = instance.GetMethodDesc(targetName, callingContext, quiet, out is_caller_method); if (routineDesc == null) { return(false); } state = (is_caller_method ? State.BoundToCaller : State.Bound); return(true); } } return(true); }
private void InvokeCallMethod(DynamicMetaObject target, DynamicMetaObject /*!*/[] args, DObject /*!*/ obj, DRoutineDesc /*!*/ method, out BindingRestrictions restrictions, out Expression invokeMethodExpr) { var insideCaller = Expression.Property( Expression.Convert(target.Expression, Types.DObject[0]), Properties.DObject_InsideCaller); if (argsArrayVariable == null) { argsArrayVariable = Expression.Parameter(Types.PhpArray[0], "args"); } if (retValVariable == null) { retValVariable = Expression.Parameter(Types.Object[0], "retVal"); } ParameterExpression[] vars = new ParameterExpression[] { argsArrayVariable, retValVariable }; // Just select real method arguments without ScriptContext and generic type arguments var justParams = BinderHelper.PackToExpressions(args, 1 + _genericParamsCount, _paramsCount); // Expression which calls ((PhpArray)argArray).add method on each real method argument. var initArgsArray = Array.ConvertAll <Expression, Expression>(justParams, (x) => Expression.Call(argsArrayVariable, Methods.PhpHashtable_Add, x)); // Argfull __call signature: (ScriptContext, object, object)->object var callerMethodArgs = new DynamicMetaObject[3] { args[0], new DynamicMetaObject(Expression.Constant(ActualMethodName), BindingRestrictions.Empty), new DynamicMetaObject(argsArrayVariable, BindingRestrictions.Empty) }; // what if method.PhpRoutine is null InvokePhpMethod(target, callerMethodArgs, /*(PhpObject)target.Value, */ method.PhpRoutine, out restrictions, out invokeMethodExpr); //Expression: // if (target.insideCaller) // throw new UndefinedMethodException(); // // args = new PhpArray(paramsCount, 0); // // args.Add(arg0); // . // . // args.Add(paramsCount); // // target.insideCaller = true; // try // { // ret_val = target.__call( scriptContext, methodName, args); // } // finally // { // target.insideCaller = false; // } // return ret_val; // invokeMethodExpr = Expression.Block( vars,//local variables Expression.IfThen(Expression.Property( Expression.Convert(target.Expression, Types.DObject[0]), Properties.DObject_InsideCaller), Expression.Call(Methods.PhpException.UndefinedMethodCalled, Expression.Constant(obj.TypeName), Expression.Constant(ActualMethodName))), Expression.Assign( argsArrayVariable, Expression.New(Constructors.PhpArray.Int32_Int32, Expression.Constant(_paramsCount), Expression.Constant(0))), ((initArgsArray.Length == 0) ? (Expression)Expression.Empty() : Expression.Block(initArgsArray)), Expression.Assign(insideCaller, Expression.Constant(true)), Expression.TryFinally( //__call(caller,args) Expression.Assign(retValVariable, invokeMethodExpr), //Finally part: Expression.Assign(insideCaller, Expression.Constant(false)) ), HandleResult(retValVariable, method.PhpRoutine.ArgFullInfo.ReturnType, false)); }
private void InvokeArgLess(DynamicMetaObject target, DynamicMetaObject scriptContext, DRoutineDesc method, DynamicMetaObject[] args, out BindingRestrictions restrictions, out Expression invokeMethodExpr) { int argsWithoutScriptContext = RealMethodArgumentCount - 1; System.Reflection.MethodInfo miAddFrame = Methods.PhpStack.AddFrame.Overload(argsWithoutScriptContext); Expression[] argsExpr = null; if (miAddFrame == Methods.PhpStack.AddFrame.N) { //Create array of arguments argsExpr = new Expression[1]; argsExpr[0] = Expression.NewArrayInit(Types.Object[0], BinderHelper.PackToExpressions(args, 0, argsWithoutScriptContext)); } else { //call overload with < N arguments //argsExpr = new Expression[argsWithoutScriptContext]; argsExpr = BinderHelper.PackToExpressions(args, 0, argsWithoutScriptContext); } var stack = Expression.Field(scriptContext.Expression, Fields.ScriptContext_Stack); // scriptContext.PhpStack // PhpStack.Add( args ) // call argless stub invokeMethodExpr = Expression.Block(_returnType, Expression.Call( stack, miAddFrame, argsExpr), Expression.Assign( Expression.Field(stack, Fields.PhpStack_AllowProtectedCall), Expression.Constant(true, Types.Bool[0])), HandleResult( Expression.Call(method.ArglessStubMethod, target.Expression, stack), method.ArglessStubMethod.ReturnType)); restrictions = target.Restrictions; }
private void InvokeClrMethod(DynamicMetaObject target, DynamicMetaObject /*!*/[] args, DRoutineDesc method, out BindingRestrictions restrictions, out Expression invokeMethodExpr) { DynamicMetaObject scriptContext = args[0]; //Select arguments without scriptContext DynamicMetaObject[] realArgs = GetArgumentsRange(args, 1, RealMethodArgumentCount - 1); #if DLR_OVERLOAD_RESOLUTION // Convert arguments DynamicMetaObject[] realArgsConverted = Array.ConvertAll <DynamicMetaObject, DynamicMetaObject>(realArgs, (x) => { return(x.ToPhpDynamicMetaObject()); }); //DLR overload resolution DynamicMetaObject res = PhpBinder.Instance.CallClrMethod(method.ClrMethod, target, realArgsConverted); restriction = res.Restriction; invokeMethodExpr = res.Rule; #else // Old overload resolution // TODO: in case of zero-parameters, we can call via ArgFull InvokeArgLess(target, scriptContext, method, realArgs, out restrictions, out invokeMethodExpr); #endif }
/// <summary> /// Emits (ScriptContext, DTypeDesc) constructor. /// </summary> private static void EmitLongConstructor(PhpType /*!*/ phpType) { // (ScriptContext, DTypeDesc) constructor ConstructorBuilder ctor_builder = phpType.LongConstructorBuilder; ctor_builder.DefineParameter(1, ParameterAttributes.None, "context"); ctor_builder.DefineParameter(2, ParameterAttributes.None, "caller"); // [ this(arg1,true) ] ILEmitter cil = new ILEmitter(ctor_builder); cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.LdcI4(1); cil.Emit(OpCodes.Call, phpType.ShortConstructorInfo); if (phpType.ProxyFieldInfo != null) { // [ <proxy>.InvokeConstructor(args) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Ldfld, phpType.ProxyFieldInfo); cil.Ldarg(1); cil.Ldarg(2); cil.Emit(OpCodes.Call, Methods.DObject_InvokeConstructor); } else { // try to find constructor method and call it directly // if it is publically visible without any reason to throw a warning in runtime DRoutineDesc construct = null; // = found constructor; if not null, can be called statically without runtime checks bool constructorFound = false; // try to find constructor for (DTypeDesc type_desc = phpType.TypeDesc; type_desc != null; type_desc = type_desc.Base) { construct = type_desc.GetMethod(DObject.SpecialMethodNames.Construct); if (construct == null) { construct = type_desc.GetMethod(new Name(type_desc.MakeSimpleName())); } if (construct != null) { constructorFound = true; if (!construct.IsPublic || construct.IsStatic || construct.PhpRoutine == null || construct.PhpRoutine.ArgLessInfo == null) { construct = null; // invalid constructor found, fall back to dynamic behavior } break; } } // emit constructor call if (construct != null) { // publically visible not static constructor, can be called statically anywhere // [ __construct( this, context.Stack ) ] cil.Ldarg(FunctionBuilder.ArgThis); // this cil.Ldarg(1); cil.Emit(OpCodes.Ldfld, Emit.Fields.ScriptContext_Stack); // context.Stack cil.Emit(OpCodes.Call, construct.PhpRoutine.ArgLessInfo); // __construct cil.Emit(OpCodes.Pop); } else if (!constructorFound) // there is no ctor at all { // [ context.Stack.RemoveFrame() ] cil.Ldarg(1); cil.Emit(OpCodes.Ldfld, Emit.Fields.ScriptContext_Stack); // context.Stack cil.Emit(OpCodes.Callvirt, Emit.Methods.PhpStack.RemoveFrame); // .RemoveFrame } else { // constructor should be checked in runtime (various visibility cases) // warnings can be displayed // [ InvokeConstructor(arg2) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(1); cil.Ldarg(2); cil.Emit(OpCodes.Call, Methods.DObject_InvokeConstructor); } } cil.Emit(OpCodes.Ret); }