Example #1
0
        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;
        }
Example #2
0
        /// <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;
        }
Example #3
0
        /// <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();
        }
Example #4
0
        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);
        }
Example #5
0
        /// <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;
            }
        }
Example #6
0
        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);
        }
Example #7
0
 /// <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));
 }
Example #8
0
        /// <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;
        }
Example #9
0
        /// <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);
        }
Example #10
0
        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));
        }
Example #11
0
        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;
        }
Example #12
0
        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
        }
Example #13
0
        /// <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);
        }