private static Expression /*!*/ PeekReferenceUnchecked(PhpRoutine routine, DynamicMetaObject scriptContext, DynamicMetaObject arg, int argIndex) { // the caller may not pushed a reference although the formal argument is a reference: // it doesn't matter if called by callback: if (!Types.PhpReference[0].IsAssignableFrom(arg.LimitType)) { // caller may have pushed a runtime chain => evaluate it: if (arg.LimitType == Types.PhpRuntimeChain[0]) { //result = php_chain.GetReference(Context); return(Expression.Call( Expression.Convert(arg.Expression, Types.PhpRuntimeChain[0]), Methods.PhpRuntimeChain.GetReference, scriptContext.Expression)); } else { // the reason of copy is not exactly known (it may be returning by copy as well as passing by copy): // result = new PhpReference(PhpVariable.Copy(arg_i, CopyReason.Unknown)); ParameterExpression resultVariable = Expression.Parameter(Types.PhpReference[0], "result"); ParameterExpression[] vars = new ParameterExpression[] { resultVariable }; return(Expression.Block(vars, Expression.Assign( resultVariable, Expression.New(Constructors.PhpReference_Object, Expression.Call(Methods.PhpVariable.Copy, arg.Expression, Expression.Constant(CopyReason.Unknown)))), BinderHelper.ThrowArgumentNotPassedByRef(argIndex, routine.FullName), resultVariable)); //(MB) I'm not sure if it's necessary to execute these two in this order //Original code // //(MB) I don't have to solve this now, PhpCallback is called in a old manner. So I can just throw exception always now. // // Reports an error in the case that we are not called by callback. // Although, this error is fatal one can switch throwing exceptions off. // If this is the case the afterwards behavior will be the same as if callback was called. //if (!Callback) //{ // // warning (can invoke user code => we have to save and restore callstate): // CallState call_state = SaveCallState(); // PhpException.ArgumentNotPassedByRef(i, CalleeName); // RestoreCallState(call_state); //} } } else { return(Expression.Convert(arg.Expression, arg.LimitType)); } }
private static Expression /*!*/ PeekType(PhpRoutine routine, DynamicMetaObject scriptContext, DynamicMetaObject arg, int argIndex) { if (arg != null) { // peeks the value: return(Expression.Convert(arg.Expression, arg.LimitType)); } else { return(Expression.Block( BinderHelper.ThrowMissingTypeArgument(argIndex, routine.FullName), Expression.Constant(DTypeDesc.ObjectTypeDesc, Types.DTypeDesc[0]))); } }
private static Expression /*!*/ PeekReference(PhpRoutine routine, DynamicMetaObject scriptContext, DynamicMetaObject arg, int argIndex) { if (arg != null) { // peeks the reference: return(PeekReferenceUnchecked(routine, scriptContext, arg, argIndex)); } else { return(Expression.Block( BinderHelper.ThrowMissingArgument(argIndex, routine.FullName), Expression.New(Constructors.PhpReference_Void))); } }
private static Expression /*!*/ PeekValue(PhpRoutine routine, DynamicMetaObject scriptContext, DynamicMetaObject arg, int argIndex) { if (arg != null) { // peeks the value: return(PeekValueUnchecked(routine, scriptContext, arg, argIndex)); } else { return(Expression.Block( BinderHelper.ThrowMissingArgument(argIndex, routine.FullName), Expression.Constant(null))); } }
///// <summary> ///// Returns methodName from Args ///// </summary> ///// <param name="args"></param> ///// <returns></returns> //protected DynamicMetaObject GetRuntimeMethodName(DynamicMetaObject[] args) //{ // //if (args.Length == this.genericParamsCount + this.paramsCount + 3) // args contains ClassContext // // return args[this.genericParamsCount + this.paramsCount + 2]; // //else if (args.Length == this.genericParamsCount + this.paramsCount + 2) // // return args[this.genericParamsCount + this.paramsCount + 1]; // //throw new InvalidOperationException(); // return args[args.Length - 1]; //} protected override DynamicMetaObject /*!*/ FallbackInvokeMember( DynamicMetaObject target, DynamicMetaObject[] args) { Debug.Assert(Types.String[0].IsAssignableFrom(args[args.Length - 1].LimitType), "Wrong field name type!"); DynamicMetaObject dmoMethodName = args[args.Length - 1]; string name = PhpVariable.AsString(dmoMethodName.Value); if (name == null) { //PhpException.Throw(PhpError.Error, CoreResources.GetString("invalid_method_name")); //return new PhpReference() | null; return(DoAndReturnDefault( BinderHelper.ThrowError("invalid_method_name"), target.Restrictions)); throw new NotImplementedException(); } else { // Restriction: PhpVariable.AsString(methodName) == |methodName| BindingRestrictions restrictions = BindingRestrictions.GetExpressionRestriction( Expression.Equal( Expression.Call(Methods.PhpVariable.AsString, dmoMethodName.Expression), Expression.Constant(dmoMethodName.Value, Types.String[0]))); actualMethodName = name; //transform arguments that it doesn't contains methodName Array.Resize <DynamicMetaObject>(ref args, args.Length - 1); DynamicMetaObject result = base.FallbackInvokeMember(target, args); return(new DynamicMetaObject( result.Expression, result.Restrictions.Merge(restrictions)));//TODO: Creation of this can be saved } }
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; }
/// <summary> /// Handles the return argument of the method /// </summary> /// <remarks> /// Caller needs as returned type /// 1.) void /// => result /// 2.) PhpReference /// a.) if method returns PhpReference => result /// b.) otherwise => PhpVariable.MakeReference(PhpVariable.Copy(result, CopyReason.ReturnedByCopy)); /// 3.) otherwise /// a.) if method returns PhpReference = > result.Value /// b.) otherwise => PhpVariable.Dereference(PhpVariable.Copy(result, CopyReason.ReturnedByCopy)); /// </remarks> /// <param name="result">Result to be handled</param> /// <param name="methodReturnType">Type of the return argument the method</param> /// <param name="dereference">Dereference will be generated.</param> protected Expression /*!*/ HandleResult(Expression /*!*/ result, Type /*!*/ methodReturnType, bool dereference = true) { if (_returnType == Types.Void) { // do nothing return(result); } else if (_returnType == Types.PhpReference[0]) { if (methodReturnType == Types.PhpReference[0]) { return(result); } else { result = CopyByReturn(result); return(Expression.Call(Methods.PhpVariable.MakeReference, result)); } } else /*if (_returnType != Types.PhpReference[0])*/ { result = CopyByReturn(result); if (methodReturnType == Types.PhpReference[0]) { return(Expression.Field(Expression.Convert(result, Types.PhpReference[0]), Fields.PhpReference_Value)); } else if (dereference) { return(Expression.Call(Methods.PhpVariable.Dereference, result)); } else { // We don't need to dereference at all in this point(for argfull overload only!) // To make sure result = BinderHelper.AssertNotPhpReference(result); return(result); } } }
public static DynamicMetaObject /*!*/ Bind(string /*!*/ methodName, CallInfo /*!*/ callInfo, DynamicMetaObjectBinder /*!*/ binder, DynamicMetaObject /*!*/ target, DynamicMetaObject /*!*/[] /*!*/ args, Func <DynamicMetaObject, DynamicMetaObject[], DynamicMetaObject> /*!*/ fallback) { Debug.Assert(fallback != null); //create DMO var phpInvokeBinder = Binder.MethodCall(methodName, 0, callInfo.ArgumentCount, null, Types.Object[0]) as PhpBaseInvokeMemberBinder; if (phpInvokeBinder != null) { //Add ScriptContext.CurrentContext var context = new DynamicMetaObject(Expression.Call(Methods.ScriptContext.GetCurrentContext), BindingRestrictions.Empty); var restrictions = BinderHelper.GetSimpleInvokeRestrictions(target, args); //Value type arguments have to be boxed DynamicMetaObject[] arguments = new DynamicMetaObject[1 + args.Length]; arguments[0] = context; for (int i = 0; i < args.Length; ++i) { arguments[1 + i] = new DynamicMetaObject(WrapDynamic(args[i].Expression), args[i].Restrictions); } var result = phpInvokeBinder.Bind(target, arguments); //Unwrap result var res = new DynamicMetaObject(Unwrap(result.Expression), restrictions); return(res); } else { return(fallback(target, args));//this will never happen } }
private DynamicMetaObject /*!*/ FallbackInvokeMember(DynamicMetaObject target /*!*/, DynamicMetaObject /*!*/[] /*!*/ args) { // determine run time values and additional restrictions: DTypeDesc classContext = this._classContext; string fieldName = this._fieldName; BindingRestrictions restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType); //target.Restrictions; int currentArg = 0; if (!ClassContextIsKnown) { Debug.Assert(args.Length > currentArg, "Not enough arguments!"); Debug.Assert(args[currentArg].Value == null || Types.DTypeDesc[0].IsAssignableFrom(args[currentArg].LimitType), "Wrong class context type!"); classContext = (DTypeDesc)args[currentArg].Value; Debug.Assert(classContext == null || !classContext.IsUnknown, "Class context should be known at run time!"); restrictions = restrictions.Merge(BindingRestrictions.GetInstanceRestriction(args[currentArg].Expression, classContext)); currentArg++; } if (IsIndirect) { Debug.Assert(args.Length > currentArg, "Not enough arguments!"); Debug.Assert(Types.String[0].IsAssignableFrom(args[currentArg].LimitType), "Wrong field name type!"); fieldName = (string)args[currentArg].Value; restrictions = restrictions.Merge( BindingRestrictions.GetExpressionRestriction( Expression.Equal( args[currentArg].Expression, Expression.Constant(fieldName, Types.String[0])))); currentArg++; } // ////Debug.Assert(!(var is PhpReference) && name != null); Debug.Assert(target.HasValue && target.LimitType != Types.PhpReference[0], "Target should not be PhpReference!"); ////if (ReferenceEquals(obj, ScriptContext.SetterChainSingletonObject)) ////{ //// ScriptContext.CurrentContext.AbortSetterChain(false); //// return new PhpReference(); ////} if (WantReference && ReferenceEquals(target.Value, ScriptContext.SetterChainSingletonObject)) { // GetObjectPropertyRef: Func <PhpReference> abortSetterChain = () => { ScriptContext.CurrentContext.AbortSetterChain(false); return(new PhpReference()); }; return(new DynamicMetaObject( Expression.Call(abortSetterChain.Method), BindingRestrictions.GetInstanceRestriction(target.Expression, ScriptContext.SetterChainSingletonObject) )); } DObject obj; ////// a property of a DObject: if ((obj = target.Value as DObject) != null) { if (obj is ClrObject /*|| obj is IClrValue // IClrValue -> ClrValue<T> -> already in restriction */) { // ((DObject)target).RealType == <obj>.RealType restrictions = restrictions.Merge( BindingRestrictions.GetInstanceRestriction( Expression.Property(Expression.Convert(target.Expression, Types.DObject[0]), Properties.DObject_RealType), obj.RealType)); } //// return GetObjectProperty(obj, name, caller, quiet); DPropertyDesc property; GetMemberResult result = obj.TypeDesc.GetInstanceProperty(new VariableName(fieldName), classContext, out property); switch (result) { case GetMemberResult.OK: ////object value = property.Get(this); ////PhpReference reference = value as PhpReference; if (property.Member is PhpField || property.Member is PhpVisibleProperty) { var realType = property.DeclaringType.RealType; FieldInfo realField = (property.Member is PhpField) ? property.PhpField.RealField : null; PropertyInfo realProperty = (property.Member is PhpVisibleProperty) ? ((PhpVisibleProperty)property.Member).RealProperty : null; Debug.Assert(realField != null ^ realProperty != null); MemberExpression getter = null; if (realField != null) { getter = Expression.Field(Expression.Convert(target.Expression, realType), realField); } else if (realProperty != null) { getter = Expression.Property(Expression.Convert(target.Expression, realType), realProperty); } if (Types.PhpReference[0].IsAssignableFrom(getter.Type)) { var reference = Expression.Variable(Types.PhpReference[0]); var assignment = Expression.Assign(reference, getter); if (WantReference) { ////value = property.Get(this); ////reference = value as PhpReference; var returnLabel = Expression.Label(this._returnType); ////if (reference != null && reference.IsSet) ////{ //// reference.IsAliased = true; //// return reference; ////} var isset = Expression.IfThen( Expression.Property(assignment, Properties.PhpReference_IsSet), Expression.Block( Expression.Assign(Expression.Property(reference, Properties.PhpReference_IsAliased), Expression.Constant(true)), Expression.Return(returnLabel, reference))); ////// the CT property has been unset -> try to invoke __get ////PhpReference get_ref = InvokeGetterRef(name, caller, out getter_exists); ////if (getter_exists) return (get_ref == null ? new PhpReference() : get_ref); ////if (reference == null) ////{ //// reference = new PhpReference(value); //// property.Set(this, reference); ////} ////else ////{ //// reference.IsAliased = true; //// reference.IsSet = true; ////} Func <DObject, string, DTypeDesc, PhpReference, PhpReference> notsetOperation = (self, name, caller, refrnc) => { bool getter_exists; // the CT property has been unset -> try to invoke __get PhpReference get_ref = self.InvokeGetterRef(name, caller, out getter_exists); if (getter_exists) { return(get_ref ?? new PhpReference()); } Debug.Assert(refrnc != null); refrnc.IsAliased = true; refrnc.IsSet = true; return(refrnc); }; ////return reference; return(new DynamicMetaObject( Expression.Block(this._returnType, new[] { reference }, new Expression[] { isset, Expression.Label(returnLabel, Expression.Call(null, notsetOperation.Method, Expression.Convert(target.Expression, Types.DObject[0]), Expression.Constant(fieldName), Expression.Constant(classContext, Types.DTypeDesc[0]), reference)) }), restrictions)); } else { ////if (reference != null && !reference.IsSet) ////{ //// // the property is CT but has been unset //// if (issetSemantics) //// { //// bool handled; //// return PropertyIssetHandler(name, caller, out handled); //// } //// else return GetRuntimeField(name, caller); ////} ////else return value; Func <DObject, string, DTypeDesc, object> notsetOperation; if (_issetSemantics) { notsetOperation = (self, name, caller) => { return(PhpVariable.Dereference(self.GetRuntimeField(name, caller))); } } ; else { notsetOperation = (self, name, caller) => { bool handled; return(PhpVariable.Dereference(self.PropertyIssetHandler(name, caller, out handled))); } }; var value = Expression.Block(this._returnType, new[] { reference }, Expression.Condition( Expression.Property(assignment, Properties.PhpReference_IsSet), Expression.Field(reference, Fields.PhpReference_Value), Expression.Call(null, notsetOperation.Method, Expression.Convert(target.Expression, Types.DObject[0]), Expression.Constant(fieldName), Expression.Constant(classContext, Types.DTypeDesc[0])) )); return(new DynamicMetaObject(value, restrictions)); } } else { if (WantReference) { return(new DynamicMetaObject( Expression.New(Constructors.PhpReference_Object, Expression.Convert(getter, Types.Object[0])), restrictions)); } else { return(new DynamicMetaObject( Expression.Call(Methods.PhpVariable.Dereference, Expression.Convert(getter, Types.Object[0])), restrictions)); } } } else if (property.Member is ClrProperty) { var realType = property.DeclaringType.RealType; var realProperty = property.ClrProperty.RealProperty; // (target.{RealObject|realValue}).<realProperty> Expression value = Expression.Convert( BinderHelper.ClrObjectWrapDynamic( Expression.Property( BinderHelper.ClrRealObject(target, realType), realProperty)), Types.Object[0]); if (WantReference) { value = BinderHelper.MakePhpReference(value); } return(new DynamicMetaObject(value, restrictions)); } else if (property.Member is ClrField) { var realType = property.DeclaringType.RealType; var realField = property.ClrField.FieldInfo; // (target.{RealObject|realValue}).<realField> Expression value = Expression.Convert( BinderHelper.ClrObjectWrapDynamic( Expression.Field( BinderHelper.ClrRealObject(target, realType), realField)), Types.Object[0]); if (WantReference) { value = BinderHelper.MakePhpReference(value); } return(new DynamicMetaObject(value, restrictions)); } else if (property.Member is ClrEvent) { var clrEvent = (ClrEvent)property.Member; var realType = property.DeclaringType.RealType; // emit stub that Wraps event as [ ClrEventObject<handlerType>.Wrap(<SC>, <event name>, <addMethod>, <removeMethod>) ] var stub = new System.Reflection.Emit.DynamicMethod( string.Format("event<{0}>", fieldName), Types.DObject[0], new[] { realType }, realType); var il = new ILEmitter(stub); clrEvent.EmitGetEventObject( il, new Place(null, Properties.ScriptContext_CurrentContext), new IndexedPlace(PlaceHolder.Argument, 0), false); il.Emit(System.Reflection.Emit.OpCodes.Ret); Expression value = Expression.Call(stub, BinderHelper.ClrRealObject(target, realType)); if (WantReference) { value = BinderHelper.MakePhpReference(value); } return(new DynamicMetaObject(value, restrictions)); } else { throw new NotImplementedException(); } case GetMemberResult.NotFound: if (WantReference) { Func <DObject, string, DTypeDesc, PhpReference> op = (self, name, caller) => { PhpReference reference; bool getter_exists; // search in RT fields if (self.RuntimeFields != null && self.RuntimeFields.ContainsKey(name)) { var namekey = new IntStringKey(name); return(self.RuntimeFields.table._ensure_item_ref(ref namekey, self.RuntimeFields)); } // property is not present -> try to invoke __get reference = self.InvokeGetterRef(name, caller, out getter_exists); if (getter_exists) { return((reference == null) ? new PhpReference() : reference); } // (no notice/warning/error thrown by PHP) // add the field reference = new PhpReference(); if (self.RuntimeFields == null) { self.RuntimeFields = new PhpArray(); } self.RuntimeFields[name] = reference; return(reference); }; return(new DynamicMetaObject( Expression.Call(null, op.Method, Expression.Convert(target.Expression, Types.DObject[0]), Expression.Constant(fieldName), Expression.Constant(classContext, Types.DTypeDesc[0])), restrictions)); } else { ////if (issetSemantics) ////{ //// OrderedHashtable<string>.Element element; //// if (RuntimeFields != null && (element = RuntimeFields.GetElement(name)) != null) //// { //// return element.Value; //// } //// else //// { //// bool handled; //// return PropertyIssetHandler(name, caller, out handled); //// } ////} ////else return GetRuntimeField(name, caller); if (_issetSemantics) { Func <DObject, string, DTypeDesc, object> notsetOperation = (self, name, caller) => { if (self.RuntimeFields != null) { object value; if (self.RuntimeFields.TryGetValue(name, out value)) { return(value); } } bool handled; return(self.PropertyIssetHandler(name, caller, out handled)); }; return(new DynamicMetaObject( Expression.Call(Methods.PhpVariable.Dereference, Expression.Call(null, notsetOperation.Method, Expression.Convert(target.Expression, Types.DObject[0]), Expression.Constant(fieldName), Expression.Constant(classContext, Types.DTypeDesc[0]))), restrictions)); } else { return(new DynamicMetaObject( Expression.Call( Methods.PhpVariable.Dereference, Expression.Call( Expression.Convert(target.Expression, Types.DObject[0]), Methods.DObject_GetRuntimeField, Expression.Constant(fieldName), Expression.Constant(classContext, Types.DTypeDesc[0]))), restrictions)); }; } case GetMemberResult.BadVisibility: { ////PhpException.PropertyNotAccessible( //// property.DeclaringType.MakeFullName(), //// name.ToString(), //// (caller == null ? String.Empty : caller.MakeFullName()), //// property.IsProtected); string stringResourceKey = property.IsProtected ? "protected_property_accessed" : "private_property_accessed"; return(new DynamicMetaObject( Expression.Block(this._returnType, Expression.Call(null, Methods.PhpException.Throw, Expression.Constant(PhpError.Error, Types.PhpError_String[0]), Expression.Constant(CoreResources.GetString(stringResourceKey, property.DeclaringType.MakeFullName(), fieldName, (classContext == null ? String.Empty : classContext.MakeFullName())))), WantReference ? (Expression)Expression.New(Constructors.PhpReference_Void) : Expression.Constant(null) ), restrictions)); } } } ////// warnings: ////if (!quiet) // not in isset() operator only ////{ if (!_issetSemantics) { //// if (PhpVariable.IsEmpty(var)) //// // empty: //// PhpException.Throw(PhpError.Notice, CoreResources.GetString("empty_used_as_object")); //// else //// // PhpArray, string, scalar type: //// PhpException.VariableMisusedAsObject(var, false); Action <object> error = (var) => { if (PhpVariable.IsEmpty(var)) { // empty: PhpException.Throw(PhpError.Notice, CoreResources.GetString("empty_used_as_object")); } else { // PhpArray, string, scalar type: PhpException.VariableMisusedAsObject(var, false); } }; return(new DynamicMetaObject( Expression.Block(this._returnType, Expression.Call(error.Method, target.Expression), WantReference ? (Expression)Expression.New(Constructors.PhpReference_Void) : Expression.Constant(null)), (target.HasValue && target.Value == null) ? BindingRestrictions.GetInstanceRestriction(target.Expression, null) : BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType))); } ////} ////// property does not exist ////return null; return(new DynamicMetaObject( Expression.Constant(null), (target.HasValue && target.Value == null) ? BindingRestrictions.GetInstanceRestriction(target.Expression, null) : BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType))); }
/// <summary> /// This method binds rules for PhpMethod /// </summary> private void InvokePhpMethod(DynamicMetaObject /*!*/ target, DynamicMetaObject[] /*!!*/ args, /*object targetObj,*/ PhpRoutine /*!*/ routine, out BindingRestrictions restrictions, out Expression invokeMethodExpr) { Debug.Assert(target != null && target.Value != null); Debug.Assert(!(target.Value is IClrValue), "PhpRoutine should not be declared on CLR value type!"); /*if (target.Value is PhpObject) * { * // Restriction: typeof(target) == |target.TypeDesc.RealType| * var targetPhpObj = (PhpObject)target.Value; * Debug.Assert(targetPhpObj.TypeDesc.RealType == target.LimitType); * Debug.Assert(target.Value.GetType() == target.LimitType); * restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, targetPhpObj.TypeDesc.RealType); * } * else*/ Debug.Assert(typeof(ClrObject).IsSealed); // just to ensure following condition is correct if (target.Value.GetType() == typeof(ClrObject)) { target = new ClrDynamicMetaObject(target); // unwrap the real object, get restrictions restrictions = target.Restrictions; } else { Debug.Assert(target.Value.GetType() == target.LimitType); // just for sure Debug.Assert(!(target.Value is PhpObject) || ((PhpObject)target.Value).TypeDesc.RealType == target.LimitType); restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType); } BindingRestrictions argumentsRestrictions; Expression[] arguments; if (routine.Name != PHP.Core.Reflection.DObject.SpecialMethodNames.Call) { args = GetArgumentsRange(args, 0, RealMethodArgumentCount);// This can't be done when _call method is invoked //Check if method has ArgAware attribute if ((routine.Properties & RoutineProperties.IsArgsAware) != 0 || routine.IsStatic)// this is because of hack in PHP.Library.XML library static methods that can be also called like instance methods { DynamicMetaObject scriptContext = args[0]; //Select arguments without scriptContext DynamicMetaObject[] realArgs = GetArgumentsRange(args, 1, RealMethodArgumentCount - 1); InvokeArgLess(target, scriptContext, routine.RoutineDesc, realArgs, out argumentsRestrictions, out invokeMethodExpr); restrictions = restrictions.Merge(argumentsRestrictions); return; } arguments = routine.PrepareArguments(args, _genericParamsCount, _paramsCount, out argumentsRestrictions); restrictions = restrictions.Merge(argumentsRestrictions); } else { arguments = BinderHelper.PackToExpressions(args); } //((PhpObject)target)) var realObjEx = Expression.Convert(target.Expression, routine.ArgFullInfo.DeclaringType);//targetObj.TypeDesc.RealType); //ArgFull( ((PhpObject)target), ScriptContext, args, ... ) invokeMethodExpr = Expression.Call(BinderHelper.WrapInstanceMethodCall(routine.ArgFullInfo), BinderHelper.CombineArguments(realObjEx, arguments)); invokeMethodExpr = ReturnArgumentHelpers.ReturnValueConversion(routine.ArgFullInfo, invokeMethodExpr); invokeMethodExpr = HandleResult(invokeMethodExpr, routine.ArgFullInfo.ReturnType, false); }
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)); }
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(); }