Beispiel #1
0
        public virtual object getConstructor(ScriptContext /*!*/ context)
        {
            if (typedesc == null)
            {
                return(false);
            }

            DRoutineDesc method;

            if (typedesc.GetMethod(Name.SpecialMethodNames.Construct, null, out method) == GetMemberResult.NotFound)
            {
                return(false);
            }

            // construct new ReflectionClass with resolved TypeDesc
            return(new ReflectionMethod(context, true)
            {
                dtype = typedesc,
                method = method,
            });
        }
Beispiel #2
0
        /// <summary>
        /// Creates a name based on <paramref name="str"/> that does not clash with any methods in the given type desc.
        /// </summary>
        internal static string /*!*/ GetNonConflictingMethodName(DTypeDesc /*!*/ typeDesc, string /*!*/ str, out bool changed)
        {
            Name name = new Name(str);

            changed = false;
            while (typeDesc.GetMethod(name) != null)
            {
                name.Value += "_";
                changed     = true;
            }

            return(name.Value);
        }
Beispiel #3
0
        /// <summary>
        /// Attemps to find a method desc according to a given class name and method name. Used when
        /// a non-virtual dispatch is about to be performed and when a <c>array(class, method)</c>
        /// callback is being bound.
        /// </summary>
        /// <param name="requestedType">The type whose method should be returned.</param>
        /// <param name="methodName">The method name.</param>
        /// <param name="self">Current <c>$this</c>. Will be set to an instance, on which the resulting
        /// CLR method should be invoked (<B>null</B> if the CLR method is static).</param>
        /// <param name="caller"><see cref="Type"/> of the object that request the lookup.</param>
        /// <param name="context">Current <see cref="ScriptContext"/>.</param>
        /// <param name="quiet">If <B>true</B>, no exceptions will be thrown if an error occurs.</param>
        /// <param name="removeFrame">If <B>true</B>, <see cref="PhpStack.RemoveFrame"/> will be called
        /// before throwing an exception.</param>
        /// <param name="isCallerMethod">Will be set to true, if required method was not found but __callStatic was.</param>
        /// <returns>The <see cref="DRoutineDesc"/> or <B>null</B> on error.</returns>
        internal static DRoutineDesc GetStaticMethodDesc(DTypeDesc requestedType, string methodName, ref DObject self,
            DTypeDesc caller, ScriptContext context, bool quiet, bool removeFrame, out bool isCallerMethod)
        {
            Debug.Assert(requestedType != null);

            isCallerMethod = false;

            DRoutineDesc method;
            GetMemberResult result = requestedType.GetMethod(new Name(methodName), caller, out method);

            if (result == GetMemberResult.NotFound)
            {
                // if not found, perform __callStatic or __call 'magic' method lookup
                Name callMethod = (self != null && requestedType.IsAssignableFrom(self.TypeDesc)) ?
                    Name.SpecialMethodNames.Call : Name.SpecialMethodNames.CallStatic;

                if ((result = requestedType.GetMethod(callMethod, caller, out method)) != GetMemberResult.NotFound)
                {
                    isCallerMethod = true;
                }
                else
                {
                    // there is no such method in the class
                    if (removeFrame) context.Stack.RemoveFrame();
                    if (!quiet) PhpException.UndefinedMethodCalled(requestedType.MakeFullName(), methodName);

                    return null;
                }
            }

            if (result == GetMemberResult.BadVisibility)
            {
                if (removeFrame) context.Stack.RemoveFrame();
                if (!quiet)
                {
                    PhpException.MethodNotAccessible(
                        method.DeclaringType.MakeFullName(),
                        method.MakeFullName(),
                        (caller == null ? String.Empty : caller.MakeFullName()),
                        method.IsProtected);
                }
                return null;
            }

            // check whether the method is abstract
            if (method.IsAbstract)
            {
                if (removeFrame) context.Stack.RemoveFrame();
                if (!quiet) PhpException.AbstractMethodCalled(method.DeclaringType.MakeFullName(), method.MakeFullName());

                return null;
            }

            if (method.IsStatic)
            {
                self = null;
            }
            else
            {
                // check whether self is of acceptable type
                if (self != null && !method.DeclaringType.RealType.IsInstanceOfType(self.RealObject)) self = null;


                /*
                // PHP allows for static invocations of instance method
				if (self == null &&
					(requestedType.IsAbstract || !(requestedType is PhpTypeDesc)) &&
					(method.DeclaringType.IsAbstract || !(method.DeclaringType is PhpTypeDesc)))
				{
					// calling instance methods declared in abstract classes statically through abstract classes
					// is unsupported -  passing null as 'this' to such instance method could result in
					// NullReferenceException even if the method does not touch $this
					if (removeFrame) context.Stack.RemoveFrame();
					if (!quiet)
					{
						PhpException.Throw(PhpError.Error, CoreResources.GetString("nonstatic_method_called_statically",
							method.DeclaringType.MakeFullName(), method.MakeFullName()));
					}
					return null;
				}

				if (self == null)
				{
                    if (!quiet && !context.Config.Variables.ZendEngineV1Compatible)
                    {
                        PhpException.Throw(PhpError.Strict, CoreResources.GetString("nonstatic_method_called_statically",
                            method.DeclaringType.MakeFullName(), method.MakeFullName()));
                    }

					// create a dummy instance to be passed as 'this' to the instance method
					DTypeDesc dummy_type =
						(!requestedType.IsAbstract && requestedType is PhpTypeDesc) ? requestedType : method.DeclaringType;

					self = PhpFunctionUtils.InvokeConstructor(
                        dummy_type,
                        //Emit.Types.ScriptContext_Bool,
                        context, false);
				}*/


                //
                // The code above was commented and replaced with following.
                //
                // We can call instance method, and pass null as 'this', and expect
                // it can fail with NullReferenceException (even if the method does not touch $this).
                // 
                // Note this solution has no side effect as above - invoking constructor of dummy instance.
                //

                // !! self can be null !!

                if (self == null)
                {
                    if (!quiet /*&& !context.Config.Variables.ZendEngineV1Compatible*/)
                    {
                        PhpException.Throw(PhpError.Strict, CoreResources.GetString("nonstatic_method_called_statically",
                            method.DeclaringType.MakeFullName(), method.MakeFullName()));
                    }
                }

            }

            return method;
        }
Beispiel #4
0
		/// <summary>
		/// Creates a name based on <paramref name="str"/> that does not clash with any methods in the given type desc.
		/// </summary>
		internal static string/*!*/ GetNonConflictingMethodName(DTypeDesc/*!*/ typeDesc, string/*!*/ str, out bool changed)
		{
			Name name = new Name(str);

			changed = false;
			while (typeDesc.GetMethod(name) != null)
			{
                name = new Name(name.Value + "_");
				changed = true;
			}

			return name.Value;
		}
Beispiel #5
0
        protected override DynamicMetaObject /*!*/ FallbackInvokeMember(DynamicMetaObject target /*!*/, DynamicMetaObject /*!*/[] /*!*/ args)
        {
            Expression invokeMethodExpr;

            DObject obj = target.Value as DObject;// target.Value can be something else which isn't DObject ?

            WrappedClrDynamicMetaObject wrappedTarget = null;

            bool invokeCallMethod = false;

            // Restrictions
            BindingRestrictions restrictions;
            BindingRestrictions classContextRestrictions = BindingRestrictions.Empty;
            BindingRestrictions defaultRestrictions      = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType);

            DTypeDesc classContext = this._classContext;

            if (!ClassContextIsKnown)//ClassContext wasn't supplied during creation of binder => put it into restriction
            {
                Debug.Assert(args.Length > RealMethodArgumentCount, "Not enough arguments!");

                DynamicMetaObject dmoRuntimeClassContext = GetRuntimeClassContext(args);
                Debug.Assert(dmoRuntimeClassContext.Value == null || Types.DTypeDesc[0].IsAssignableFrom(dmoRuntimeClassContext.LimitType), "Wrong class context type!");

                classContext = (DTypeDesc)dmoRuntimeClassContext.Value;
                Debug.Assert(classContext == null || !classContext.IsUnknown, "Class context should be known at run time!");

                classContextRestrictions = BindingRestrictions.GetInstanceRestriction(dmoRuntimeClassContext.Expression, classContext);
                defaultRestrictions      = defaultRestrictions.Merge(classContextRestrictions);
            }

            if (obj == null)
            {
                if (target.Value != null && Configuration.Application.Compiler.ClrSemantics)
                {
                    // TODO: some normalizing conversions (PhpString, PhpBytes -> string):
                    target = new WrappedClrDynamicMetaObject(target);
                    obj    = target.Value as DObject;

                    wrappedTarget = target as WrappedClrDynamicMetaObject;

                    Debug.Assert(obj != null);
                }
                else
                {
                    //defaultRestrictions = defaultRestrictions.Merge(BindingRestrictions.GetTypeRestriction
                    if (target.Value == null)
                    {
                        defaultRestrictions = BindingRestrictions.GetInstanceRestriction(target.Expression, null);
                    }

                    return(DoAndReturnDefault(
                               BinderHelper.ThrowError("method_called_on_non_object", ActualMethodName),
                               defaultRestrictions));
                }
            }



            // obtain the appropriate method table
            DTypeDesc type_desc = obj.TypeDesc;

            // perform method lookup
            DRoutineDesc    method;
            GetMemberResult result = type_desc.GetMethod(new Name(ActualMethodName), classContext, out method);

            //PhpStack stack = context.Stack;

            if (result == GetMemberResult.NotFound)
            {
                if ((result = type_desc.GetMethod(DObject.SpecialMethodNames.Call, classContext, out method)) == GetMemberResult.NotFound)
                {
                    return(DoAndReturnDefault(
                               Expression.Call(Methods.PhpException.UndefinedMethodCalled, Expression.Constant(obj.TypeName), Expression.Constant(ActualMethodName)),
                               defaultRestrictions
                               ));    // TODO: alter restrictions
                }
                else
                {
                    invokeCallMethod = true;
                }
            }

            // throw an error if the method was found but the caller is not allowed to call it due to its visibility
            if (result == GetMemberResult.BadVisibility)
            {
                return(DoAndReturnDefault(
                           BinderHelper.ThrowVisibilityError(method, classContext),
                           defaultRestrictions));
            }

            if (invokeCallMethod)
            {
                InvokeCallMethod(target, args, obj, method, out restrictions, out invokeMethodExpr);

                return(new DynamicMetaObject(invokeMethodExpr, restrictions.Merge(classContextRestrictions)));
            }
            else
            {
                // we are invoking the method

                // PhpRoutine (function or method)
                if (method.Member is PhpRoutine)
                {
                    InvokePhpMethod(target, args, method.PhpRoutine, out restrictions, out invokeMethodExpr);
                    return(new DynamicMetaObject(invokeMethodExpr, restrictions.Merge(classContextRestrictions)));
                }
                // ClrMethod
                else if (method.Member is ClrMethod)
                {
                    var targetwrapper = (target.LimitType == typeof(ClrObject)) ?
                                        (DynamicMetaObject) new ClrDynamicMetaObject(target) :     // ((ClrObject)target).RealType restriction
                                        (DynamicMetaObject) new ClrValueDynamicMetaObject(target); // simple type restriction, IClrValue<T> or any .NET class inheriting PhpObject

                    InvokeClrMethod(targetwrapper, args, method, out restrictions, out invokeMethodExpr);

                    if (wrappedTarget != null)
                    {
                        return(new DynamicMetaObject(Expression.Block(wrappedTarget.WrapIt(),
                                                                      invokeMethodExpr), wrappedTarget.Restrictions.Merge(classContextRestrictions)));
                    }

                    return(new DynamicMetaObject(invokeMethodExpr, restrictions.Merge(classContextRestrictions)));
                }
            }

            throw new NotImplementedException();
        }
Beispiel #6
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);
        }