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, }); }
/// <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); }
/// <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; }
/// <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; }
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(); }
/// <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); }