protected virtual Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameExpr, DynamicMetaObject target, /*in, out*/ IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { string nameText = NameValue; if (nameText == null) { if (nameExpr != null) { nameText = nameExpr.Value.ToString(); } else { nameText = "<unknown>"; } } // TODO: ErrCode method not found throw new ArgumentException(string.Format("Function '{0}' not found!", nameText)); }
/// <summary> /// Selects only candidates of given name. /// </summary> public static MethodInfo[] SelectRuntimeMethods(this PhpTypeInfo tinfo, string name, Type classCtx) { var routine = (PhpMethodInfo)tinfo?.RuntimeMethods[name]; if (routine != null) { return(routine.Methods); } else { if (classCtx != null && tinfo.Type.IsSubclassOf(classCtx)) { // {tinfo} extends {classCtx} // we might have to look for private methods on {classCtx} return(SelectRuntimeMethods(classCtx.GetPhpTypeInfo(), name, null)); } return(Array.Empty <MethodInfo>()); } }
public static PhpInvokable BindToPhpInvokable(MethodInfo[] methods, PhpTypeInfo lateStaticType = null) { // (Context ctx, object target, PhpValue[] arguments) var ps = new ParameterExpression[] { Expression.Parameter(typeof(Context), "ctx"), Expression.Parameter(typeof(object), "target"), Expression.Parameter(typeof(PhpValue[]), "argv") }; // invoke targets var invocation = OverloadBinder.BindOverloadCall(typeof(PhpValue), ps[1], methods, ps[0], ps[2], lateStaticType); Debug.Assert(invocation.Type == typeof(PhpValue)); // compile & create delegate var lambda = Expression.Lambda <PhpInvokable>(invocation, methods[0].Name + "#" + methods.Length, true, ps); return(lambda.Compile()); }
PhpCallable BindCore(PhpTypeInfo tinfo) { var routine = (PhpMethodInfo)tinfo?.RuntimeMethods[_method]; if (routine != null) { return(routine.PhpInvokable.Bind(null)); } else if (tinfo != null) { routine = (PhpMethodInfo)tinfo.RuntimeMethods[TypeMethods.MagicMethods.__callstatic]; if (routine != null) { return(routine.PhpInvokable.BindMagicCall(null, _method)); } } return(null); }
public void __construct(Context ctx, PhpValue @class, string name) { if (name != null) { _tinfo = ReflectionUtils.ResolvePhpTypeInfo(ctx, @class); _routine = _tinfo.RuntimeMethods[name] ?? throw new ReflectionException(string.Format(Resources.Resources.method_does_not_exist, _tinfo.Name, name)); } else if (@class.IsString(out var class_method)) { __construct(ctx, class_method); } else { throw new ReflectionException(string.Format("Invalid method name '{0}'", @class.ToString(ctx))); } // get the real declaring type from routine: _tinfo = _routine.DeclaringType ?? _tinfo; }
public void __construct(Context ctx, string class_method) { if (class_method != null) { var col = class_method.IndexOf("::", StringComparison.Ordinal); if (col > 0) { var @class = class_method.Remove(col); // class name var name = class_method.Substring(col + 2); // method namne _tinfo = ctx.GetDeclaredTypeOrThrow(@class, true); _routine = _tinfo.RuntimeMethods[name] ?? throw new ReflectionException(string.Format(Resources.Resources.method_does_not_exist, _tinfo.Name, name)); // ok return; } } throw new ReflectionException(); }
protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { var call = BinderHelpers.FindMagicMethod(tinfo, target == null ? TypeMethods.MagicMethods.__callstatic : TypeMethods.MagicMethods.__call); if (call != null) { var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression; // T.__callStatic(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)), }; return(OverloadBinder.BindOverloadCall(_returnType, target?.Expression, call.Methods, ctx.Expression, call_args)); } // return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions)); }
protected override MethodBase[] ResolveMethods(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameExpr, ref DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { // resolve target expression: var isobject = BinderHelpers.TryTargetAsObject(target, out DynamicMetaObject instance); restrictions = restrictions.Merge(instance.Restrictions); target = new DynamicMetaObject(Expression.Convert(instance.Expression, instance.RuntimeType), target.Restrictions, instance.Value); if (isobject == false) { return(null); // no methods } string name = _name ?? (string)nameExpr.Value; // candidates: var candidates = target.RuntimeType.GetPhpTypeInfo().SelectRuntimeMethods(name).SelectVisible(_classCtx).ToList(); return(candidates.ToArray()); }
/// <summary> /// Default SPL autoload that tries to load scripts with name of class name concatenated with autoload extensions. /// </summary> public static void spl_autoload(Context ctx, string className) { var extensions = ctx.GetSplAutoload()?.AutoloadExtensions ?? SplAutoloadService.DefaultAutoloadExtensions; PhpTypeInfo resolved = null; for (int i = 0; (resolved = ctx.GetDeclaredType(className)) == null && i < extensions.Length; i++) { var ext = extensions[i]; // try to dynamically include the file specified by the class name, if it exists string fileName = className + ext; ctx.Include(null, fileName, true, false); } if (resolved == null) { //PhpException.Throw(PhpError.Error, string.Format(CoreResources.class_could_not_be_loaded, className)); throw new InvalidOperationException("class_could_not_be_loaded"); } }
/// <summary> /// Performs autoload. /// </summary> public PhpTypeInfo AutoloadTypeByName(string fullName) { PhpTypeInfo resolved = null; using (var token = new Context.RecursionCheckToken(_ctx, fullName)) { if (!token.IsInRecursion) { var args = new[] { (PhpValue)fullName }; for (var node = _autoloaders.First; node != null && resolved == null; node = node.Next) { node.Value.Invoke(_ctx, args); resolved = _ctx.GetDeclaredType(fullName); } } } // return(resolved); }
protected override MethodBase[] ResolveMethods(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameExpr, ref DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { // check tinfo is assignable from target if (target?.Value == null || !tinfo.Type.IsAssignableFrom(target.LimitType)) { target = null; } // var nameStr = _name ?? (string)nameExpr.Value; if (nameStr != null) { // candidates: var candidates = tinfo.SelectRuntimeMethods(nameStr).SelectVisible(_classCtx).SelectStatic().ToList(); return(candidates.ToArray()); } else { throw new NotImplementedException(); } }
protected override Expression BindMissingMethod(DynamicMetaObject ctx, PhpTypeInfo tinfo, DynamicMetaObject nameMeta, DynamicMetaObject target, IList <DynamicMetaObject> args, ref BindingRestrictions restrictions) { var name_expr = (_name != null) ? Expression.Constant(_name) : nameMeta?.Expression; // resolve target expression: Expression target_expr; object target_value; BinderHelpers.TargetAsObject(target, out target_expr, out target_value, ref restrictions); if (target_value == null) { /* Template: * PhpException.MethodOnNonObject(name_expr); // aka PhpException.Throw(Error, method_called_on_non_object, name_expr) * return NULL; */ var throwcall = Expression.Call(typeof(PhpException), "MethodOnNonObject", Array.Empty <Type>(), ConvertExpression.Bind(name_expr, typeof(string), ctx.Expression)); return(Expression.Block(throwcall, ConvertExpression.BindDefault(this.ReturnType))); } Debug.Assert(ReflectionUtils.IsClassType(target_value.GetType().GetTypeInfo())); tinfo = target_value.GetPhpTypeInfo(); var call = BinderHelpers.FindMagicMethod(tinfo, TypeMethods.MagicMethods.__call); if (call != null) { // target.__call(name, array) var call_args = new Expression[] { name_expr, BinderHelpers.NewPhpArray(ctx.Expression, args.Select(a => a.Expression)), }; return(OverloadBinder.BindOverloadCall(_returnType, target.Expression, call.Methods, ctx.Expression, call_args)); } return(base.BindMissingMethod(ctx, tinfo, nameMeta, target, args, ref restrictions)); }
PhpCallable BindCore(PhpTypeInfo tinfo) { var routine = (PhpMethodInfo)tinfo?.RuntimeMethods[_method]; if (routine != null) { return(routine.PhpInvokable.Bind(null)); } else if (tinfo != null) { routine = (PhpMethodInfo)tinfo.RuntimeMethods[TypeMethods.MagicMethods.__callstatic]; if (routine != null) { return(routine.PhpInvokable.BindMagicCall(null, _method)); } } Debug.WriteLine($"Method '{_class}::{_method}' is not defined!"); // TODO: ctx.Error.CallToUndefinedMethod(_function) return(null); }
/// <summary> /// Gets <see cref="PhpTypeInfo"/> of parent. /// Throws in case of parent being used out of class context or within a parentless class. /// </summary> public static PhpTypeInfo GetParent(PhpTypeInfo self) { if (self == null) { PhpException.Throw(PhpError.Error, Resources.ErrResources.parent_used_out_of_class); } else { var t = self.BaseType; if (t != null) { return(t); } else { PhpException.Throw(PhpError.Error, Resources.ErrResources.parent_accessed_in_parentless_class); } } // throw new ArgumentException(nameof(self)); }
IEnumerable <KeyValuePair <string, PhpValue> > EnumerateSerializableProperties(object obj, PhpTypeInfo tinfo, PhpArray properties) { Debug.Assert(obj != null); Debug.Assert(tinfo != null); Debug.Assert(properties != null); PhpArray runtime_fields = null; var enumerator = properties.GetFastEnumerator(); while (enumerator.MoveNext()) { FieldAttributes visibility; string name = enumerator.CurrentValue.ToStringOrThrow(_ctx); string declaring_type_name; string property_name = Serialization.ParseSerializedPropertyName(name, out declaring_type_name, out visibility); PhpTypeInfo declarer; // for visibility check if (declaring_type_name == null) { declarer = tinfo; } else { declarer = _ctx.GetDeclaredType(declaring_type_name); if (declarer == null) { // property name refers to an unknown class -> value will be null yield return(new KeyValuePair <string, PhpValue>(name, PhpValue.Null)); continue; } } // obtain the property desc and decorate the prop name according to its visibility and declaring class var property = tinfo.GetDeclaredProperty(property_name); if (property != null && !property.IsStatic && property.IsVisible(declarer.Type)) { // if certain conditions are met, serialize the property as null // (this is to precisely mimic the PHP behavior) if ((visibility == (property.Attributes & FieldAttributes.FieldAccessMask) && visibility != FieldAttributes.Public) || (visibility == FieldAttributes.Private && declarer != property.ContainingType)) { yield return(new KeyValuePair <string, PhpValue>(name, PhpValue.Null)); continue; } name = Serialization.FormatSerializedPropertyName(property); } else { property = null; // field is not visible, try runtime fields } // obtain the property value PhpValue val; if (property != null) { val = property.GetValue(_ctx, obj); } else { if (runtime_fields == null) { runtime_fields = tinfo.GetRuntimeFields(obj) ?? PhpArray.Empty; } if (!runtime_fields.TryGetValue(name, out val)) { // PHP 5.1+ PhpException.Throw(PhpError.Notice, string.Format(Core.Resources.ErrResources.sleep_returned_bad_field, name)); } } yield return(new KeyValuePair <string, PhpValue>(name, val)); } }
/// <summary> /// Declare a runtime user type unser an aliased name. /// </summary> /// <param name="tinfo">Original type descriptor.</param> /// <param name="typename">Type name alias, can differ from <see cref="PhpTypeInfo.Name"/>.</param> public void DeclareType(PhpTypeInfo tinfo, string typename) => _types.DeclareTypeAlias(tinfo, typename);
/// <summary> /// Creates expression with overload resolution. /// </summary> /// <param name="treturn">Expected return type.</param> /// <param name="target">Target instance in case of instance methods.</param> /// <param name="methods">List of methods to resolve overload.</param> /// <param name="ctx">Expression of current runtime context.</param> /// <param name="args">Provides arguments.</param> /// <param name="lateStaticType">Optional type used to statically invoke the method (late static type).</param> /// <returns>Expression representing overload call with resolution or <c>null</c> in case binding should be restarted with updated array of <paramref name="methods"/>.</returns> static Expression BindOverloadCall(Type treturn, Expression target, ref MethodBase[] methods, Expression ctx, ArgumentsBinder args, PhpTypeInfo lateStaticType = null) { if (methods == null || args == null) { throw new ArgumentNullException(); } // overload resolution // order methods /* * cost1 = CostOf(m1) * cost2 = CostOf(m2) * ... * best = Min(cost1, .., costN) * if (cost1 == best) m1( ... ) * ... * default(T) // unreachable */ var locals = new List <ParameterExpression>(); var body = new List <Expression>(); Expression invoke = Expression.Default(treturn); // if (methods.Length == 0) { throw new ArgumentException(); // no method to call // invoke = ERR } if (methods.Length == 1) { // just this piece of code is enough: invoke = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, methods[0], ctx, args, lateStaticType), DoCastToFalse(methods[0], treturn)), treturn, ctx); } else { var list = new List <MethodCostInfo>(); // collect arguments, that have same type across all provided methods => we don't have to check costof() var makeCostOf = DifferentArgumentTypeIndexes(methods); // parameters which costof() have to be calculated and compared to others // costX = CostOf(mX) foreach (var m in methods) { ConversionCost mincost; // minimal cost resolved in compile time var expr_cost = BindCostOf(m, args, makeCostOf, out mincost); if (mincost >= ConversionCost.NoConversion) { continue; // we don't have to try this overload } var const_cost = expr_cost as ConstantExpression; var cost_var = Expression.Variable(typeof(ConversionCost), "cost" + list.Count); if (const_cost == null) { body.Add(Expression.Assign(cost_var, expr_cost)); // costX = CostOf(m) } list.Add(new MethodCostInfo() { Method = m, CostExpr = (Expression)const_cost ?? cost_var, MinimalCost = mincost, }); } if (list.Count != 0) { var minresolved = ConversionCost.Error; foreach (var c in list.Where(c => c.ResolvedCost.HasValue)) { minresolved = CostOf.Min(minresolved, c.ResolvedCost.Value); } if (list.All(c => c.ResolvedCost.HasValue)) { for (int i = 0; i < list.Count; i++) { if (list[i].ResolvedCost.Value == minresolved) { list = new List <MethodCostInfo>() { list[i] }; break; } } } else { // if minimal cost is greater than some resolved cost, we can reduce it if (minresolved < ConversionCost.Error) { for (int i = list.Count - 1; i >= 0; i--) { if (list[i].MinimalCost > minresolved) { list.RemoveAt(i); } } } } } if (list.Count < methods.Length) { // restart binding with reduced list methods = list.Select(l => l.Method).ToArray(); return(null); } Debug.Assert(list.Count != 0); // declare costI local variables locals.AddRange(list.Select(l => l.CostExpr).OfType <ParameterExpression>()); // best = Min( cost1, .., costN ) var expr_best = Expression.Variable(typeof(ConversionCost), "best"); var min_cost_cost = typeof(CostOf).GetMethod("Min", typeof(ConversionCost), typeof(ConversionCost)); Expression minexpr = list[0].CostExpr; for (int i = 1; i < list.Count; i++) { minexpr = Expression.Call(min_cost_cost, list[i].CostExpr, minexpr); } body.Add(Expression.Assign(expr_best, minexpr)); locals.Add(expr_best); // switch over method costs for (int i = list.Count - 1; i >= 0; i--) { // (best == costI) mI(...) : ... var m = list[i].Method; var mcall = ConvertExpression.Bind(BindCastToFalse(BinderHelpers.BindToCall(target, m, ctx, args, lateStaticType), DoCastToFalse(m, treturn)), treturn, ctx); invoke = Expression.Condition(Expression.Equal(expr_best, list[i].CostExpr), mcall, invoke); } } // body.Insert(0, args.CreatePreamble(locals)); // body.Add(invoke); // return Block { ... ; invoke; } return(Expression.Block(treturn, locals, body)); }
public static string get_called_class([ImportCallerStaticClass] PhpTypeInfo @static) => @static?.Name;
public static Expression BindOverloadCall(Type treturn, Expression target, MethodBase[] methods, Expression ctx, Expression argsarray, bool isStaticCallSyntax, PhpTypeInfo lateStaticType = null) { Expression result = null; while (result == null) { result = BindOverloadCall(treturn, target, ref methods, ctx, new ArgumentsBinder.ArgsArrayBinder(ctx, argsarray), isStaticCallSyntax, lateStaticType); } return(result); }
public static Expression BindOverloadCall(Type treturn, Expression target, MethodBase[] methods, Expression ctx, Expression[] args, PhpTypeInfo lateStaticType = null) { Expression result = null; while (result == null) { result = BindOverloadCall(treturn, target, ref methods, ctx, new ArgumentsBinder.ArgsBinder(ctx, args), lateStaticType); } return(result); }
public static PhpMethodInfo FindMagicMethod(PhpTypeInfo type, TypeMethods.MagicMethods magic) { return((PhpMethodInfo)type.RuntimeMethods[magic]); }
public static Expression BindField(PhpTypeInfo type, Type classCtx, Expression target, string field, Expression ctx, AccessMask access, Expression rvalue) { if (access.Write() != (rvalue != null)) { throw new ArgumentException(); } // lookup a declared field for (var t = type; t != null; t = t.BaseType) { foreach (var p in t.DeclaredFields.GetPhpProperties(field)) { if (p.IsStatic == (target == null) && p.IsVisible(classCtx)) { return(BindAccess(p.Bind(ctx, target), ctx, access, rvalue)); } } } // // runtime fields // if (type.RuntimeFieldsHolder != null) // we don't handle magic methods without the runtime fields { var runtimeflds = Expression.Field(target, type.RuntimeFieldsHolder); // Template: target->__runtime_fields var fieldkey = Expression.Constant(new IntStringKey(field)); // Template: IntStringKey(field) var resultvar = Expression.Variable(Cache.Types.PhpValue[0], "result"); // Template: PhpValue result; // Template: runtimeflds != null && runtimeflds.TryGetValue(field, out result) var trygetfield = Expression.AndAlso(Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)), Expression.Call(runtimeflds, Cache.Operators.PhpArray_TryGetValue, fieldkey, resultvar)); var containsfield = Expression.AndAlso(Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)), Expression.Call(runtimeflds, Cache.Operators.PhpArray_ContainsKey, fieldkey)); Expression result; // if (access.EnsureObject()) { // (object)target->field-> // Template: runtimeflds.EnsureObject(key) result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemObject, fieldkey); var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null); if (__get != null) { // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureObject(key) : ( __get(key) ?? runtimeflds.EnsureObject(key)) return(Expression.Condition(containsfield, Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemObject, fieldkey), InvokeHandler(ctx, target, field, __get, access, result, typeof(object)))); } else { return(result); } } else if (access.EnsureArray()) { // (IPhpArray)target->field[] = result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemArray, fieldkey); var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null); if (__get != null) { // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureArray(key) : ( __get(key) ?? runtimeflds.EnsureArray(key)) return(Expression.Condition(containsfield, Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemArray, fieldkey), InvokeHandler(ctx, target, field, __get, access, result, typeof(IPhpArray)))); } else { // runtimeflds.EnsureItemArray(key) return(result); } } else if (access.EnsureAlias()) { // (PhpAlias)&target->field result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_EnsureItemAlias, fieldkey); var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null); if (__get != null) { // Template: runtimeflds.Contains(key) ? runtimeflds.EnsureItemAlias(key) : ( __get(key) ?? runtimeflds.EnsureItemAlias(key)) return(Expression.Condition(containsfield, Expression.Call(runtimeflds, Cache.Operators.PhpArray_EnsureItemAlias, fieldkey), InvokeHandler(ctx, target, field, __get, access, result, typeof(PhpAlias)))); } else { // runtimeflds.EnsureItemAlias(key) return(result); } } else if (access.Unset()) { // unset(target->field) // Template: if (!runtimeflds.RemoveKey(key)) __unset(key) var removekey = Expression.Call(runtimeflds, Cache.Operators.PhpArray_RemoveKey, fieldkey); var __unset = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__unset, field, null); if (__unset != null) { return(Expression.IfThen( Expression.OrElse(Expression.ReferenceEqual(runtimeflds, Expression.Constant(null)), Expression.IsFalse(removekey)), InvokeHandler(ctx, target, field, __unset, access, Expression.Block(), typeof(void)))); } else { // if (runtimeflds != null) runtimeflds.RemoveKey(key) return(Expression.IfThen( Expression.ReferenceNotEqual(runtimeflds, Expression.Constant(null)), removekey)); } } else if (access.Write()) { var __set = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__set, field, rvalue); if (access.WriteAlias()) { // target->field = (PhpAlias)&rvalue Debug.Assert(rvalue.Type == typeof(PhpAlias)); rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx); // EnsureNotNull(runtimeflds).SetItemAlias(key, rvalue) result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_SetItemAlias, fieldkey, rvalue); if (__set != null) { // if (ContainsKey(key)) ? runtimeflds.SetItemAlias(rvalue) : (__set(key, rvalue) ?? runtimeflds.SetItemAlias(key, rvalue) return(Expression.Condition(containsfield, Expression.Call(runtimeflds, Cache.Operators.PhpArray_SetItemAlias, fieldkey, rvalue), InvokeHandler(ctx, target, field, __set, access, result, typeof(void)))); } else { return(result); } } else { // target->field = rvalue rvalue = ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx); /* Template: * return runtimeflds != null && runtimeflds.ContainsKey(field) * ? runtimeflds.SetItemValue(key, rvalue) * : (__set(field, value) ?? runtimeflds.SetItemValue(key, value)) */ result = Expression.Call(EnsureNotNullPhpArray(runtimeflds), Cache.Operators.PhpArray_SetItemValue, fieldkey, rvalue); if (__set != null) { return(Expression.Condition(containsfield, Expression.Call(runtimeflds, Cache.Operators.PhpArray_SetItemValue, fieldkey, rvalue), InvokeHandler(ctx, target, field, __set, access, result, typeof(void)))); } else { return(result); } } } else if (access.Isset()) { // isset(target->field) var __isset = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__isset, field, null); // Template: (TryGetField(result) || (bool)__isset(key)) ? true : void result = Expression.Condition(Expression.OrElse( trygetfield, ConvertExpression.BindToBool(InvokeHandler(ctx, target, field, __isset, access))), Expression.Property(null, Cache.Properties.PhpValue_True), Expression.Field(null, Cache.Properties.PhpValue_Void)); } else { // = target->field /* Template: * return runtimeflds.TryGetValue(field, out result) ? result : (__get(field) ?? ERR); */ var __get = BindMagicMethod(type, classCtx, target, ctx, TypeMethods.MagicMethods.__get, field, null); result = Expression.Condition(trygetfield, resultvar, InvokeHandler(ctx, target, field, __get, access)); // TODO: @default = { ThrowError; return null; } } // return(Expression.Block(result.Type, new[] { resultvar }, result)); } // TODO: IDynamicMetaObject // return(null); }
internal bool setFetchMode(PDO_FETCH mode, ReadOnlySpan <PhpValue> args) { switch (mode & ~PDO_FETCH.Flags) { case PDO_FETCH.Default: case PDO_FETCH.FETCH_LAZY: case PDO_FETCH.FETCH_ASSOC: case PDO_FETCH.FETCH_NUM: case PDO_FETCH.FETCH_BOTH: case PDO_FETCH.FETCH_OBJ: case PDO_FETCH.FETCH_BOUND: case PDO_FETCH.FETCH_NAMED: case PDO_FETCH.FETCH_KEY_PAIR: if (args.Length != 0) { HandleError("fetch mode doesn't allow any extra arguments"); return(false); } // ok break; case PDO_FETCH.FETCH_COLUMN: if (args.Length != 1) { HandleError("fetch mode requires the colno argument"); return(false); } else if (args[0].IsLong(out var l)) { _fetch_column = (int)l; break; } else { HandleError("colno must be an integer"); return(false); } case PDO_FETCH.FETCH_CLASS: _default_fetch_class_args = default; _default_fetch_class = null; if ((mode & PDO_FETCH.FETCH_CLASSTYPE) != 0) { // will be getting its class name from 1st column if (args.Length != 0) { HandleError("fetch mode doesn't allow any extra arguments"); return(false); } else { break; } } else { if (args.Length == 0) { HandleError("fetch mode requires the classname argument"); return(false); } else if (args.Length > 2) { HandleError("too many arguments"); return(false); } else if (args[0].IsString(out var name)) { _default_fetch_class = Context.GetDeclaredTypeOrThrow(name, autoload: true); if (_default_fetch_class != null) { if (args.Length > 1) { var ctorargs = args[1].AsArray(); if (ctorargs == null && !args[1].IsNull) { HandleError("ctor_args must be either NULL or an array"); return(false); } else if (ctorargs != null && ctorargs.Count != 0) { _default_fetch_class_args = ctorargs.GetValues(); // TODO: deep copy } } break; } else { return(false); } } else { HandleError("classname must be a string"); return(false); } } //case PDO_FETCH.FETCH_INTO: // if (args.Length != 1) // { // RaiseError("fetch mode requires the object parameter"); // return false; // } // else // { // FetchClassInto = args[0].AsObject(); // if (FetchClassInto == null) // { // RaiseError("object must be an object"); // return false; // } // m_fetchStyle = mode; // return true; // } //default: // RaiseError("Invalid fetch mode specified"); // return false; default: throw new NotImplementedException(mode.ToString()); } // _default_fetch_type = mode; return(true); }
/// <summary> /// Checks the user type is declared in the current state of <see cref="Context"/>. /// </summary> /// <param name="phptype">PHP type runtime information. Must not be <c>null</c>.</param> /// <returns>True if the type has been declared on the current <see cref="Context"/>.</returns> internal bool IsUserTypeDeclared(PhpTypeInfo phptype) => _types.IsDeclared(phptype);
public static Expression BindField(PhpTypeInfo type, Type classCtx, Expression target, string field, Expression ctx, AccessFlags access, Expression rvalue) { if (access.Write() != (rvalue != null)) { throw new ArgumentException(); } // lookup a declared field for (var t = type; t != null; t = t.BaseType) { var expr = t.DeclaredFields.Bind(field, classCtx, target, ctx, (target != null) ? TypeFields.FieldKind.InstanceField : TypeFields.FieldKind.StaticField); if (expr != null) { return BindAccess(expr, ctx, access, rvalue); } } // lookup __get, __set, ... TypeMethods.MagicMethods magic; if (access.Write()) magic = TypeMethods.MagicMethods.__set; else if (access.Unset()) magic = TypeMethods.MagicMethods.__unset; else if (access.Isset()) magic = TypeMethods.MagicMethods.__isset; else magic = TypeMethods.MagicMethods.__get; for (var t = type; t != null; t = t.BaseType) { var m = (PhpMethodInfo)t.DeclaredMethods[magic]; if (m != null) { if (m.Methods.Length == 1 && m.Methods[0].IsVisible(classCtx)) { switch (magic) { case TypeMethods.MagicMethods.__set: // __set(name, value) return OverloadBinder.BindOverloadCall(rvalue.Type, target, m.Methods, ctx, new Expression[] { Expression.Constant(field), rvalue }); default: // __get(name), __unset(name), __isset(name) return OverloadBinder.BindOverloadCall(m.Methods[0].ReturnType, target, m.Methods, ctx, new Expression[] { Expression.Constant(field) }); } } break; } } // runtime fields var __runtimeflds = type.RuntimeFieldsHolder; if (__runtimeflds != null) { var __runtimeflds_field = Expression.Field(target, __runtimeflds); var key = Expression.Constant(new IntStringKey(field)); return BindArrayAccess(__runtimeflds_field, key, ctx, access, rvalue); } // TODO: dynamic // return null; }
public static Expression BindToCall(Expression instance, MethodBase method, Expression ctx, OverloadBinder.ArgumentsBinder args, PhpTypeInfo lateStaticType) { Debug.Assert(method is MethodInfo || method is ConstructorInfo); var ps = method.GetParameters(); var boundargs = new Expression[ps.Length]; int argi = 0; for (int i = 0; i < ps.Length; i++) { var p = ps[i]; if (argi == 0 && p.IsImplicitParameter()) { if (p.IsContextParameter()) { boundargs[i] = ctx; } else if (p.IsLateStaticParameter()) { if (lateStaticType != null) { // Template: PhpTypeInfoExtension.GetPhpTypeInfo<lateStaticType>() boundargs[i] = Expression.Call( null, typeof(PhpTypeInfoExtension).GetMethod("GetPhpTypeInfo", Cache.Types.Empty).MakeGenericMethod(lateStaticType.Type.AsType())); } else { throw new InvalidOperationException("static context not available."); } } else if (p.IsImportLocalsParameter()) { // no way we can implement this throw new NotImplementedException(); // TODO: empty array & report warning } else if (p.IsImportCallerArgsParameter()) { // we don't have this info throw new NotImplementedException(); // TODO: empty array & report warning } else if (p.IsImportCallerClassParameter()) { // TODO: pass classctx from the callsite throw new NotImplementedException(); } else if (p.IsImportCallerStaticClassParameter()) { throw new NotSupportedException(); // we don't know current late static bound type } else { throw new NotImplementedException(); } } else { if (i == ps.Length - 1 && p.IsParamsParameter()) { var element_type = p.ParameterType.GetElementType(); boundargs[i] = args.BindParams(argi, element_type); break; } else { boundargs[i] = args.BindArgument(argi, p); } // argi++; } } // Debug.Assert(boundargs.All(x => x != null)); // if (method.IsStatic) { instance = null; } // if (method.IsConstructor) { return(Expression.New((ConstructorInfo)method, boundargs)); } if (instance != null) { instance = Expression.Convert(instance, method.DeclaringType); } if (instance != null && method.IsVirtual) // NOTE: only needed for parent::foo(), static::foo() and self::foo() ? { // Ugly hack here, // we NEED to call the method nonvirtually, but LambdaCompiler emits .callvirt always and there is no way how to change it (except we can emit all the stuff by ourselfs). // We use DynamicMethod to emit .call inside, and use its MethodInfo which is static. // LambdaCompiler generates .call to static DynamicMethod which calls our method via .call as well, // after all the inlining, there should be no overhead. method = WrapInstanceMethodToStatic((MethodInfo)method); // var newargs = new Expression[boundargs.Length + 1]; newargs[0] = instance; Array.Copy(boundargs, 0, newargs, 1, boundargs.Length); boundargs = newargs; instance = null; } // return(Expression.Call(instance, (MethodInfo)method, boundargs)); }
public static string get_called_class([ImportValue(ImportValueAttribute.ValueSpec.CallerStaticClass)] PhpTypeInfo @static) => @static?.Name;
/// <summary> /// Calls a user defined function or method given by the function parameter. /// This function must be called within a method context, it can't be used outside a class. /// It uses the late static binding. /// All arguments of the forwarded method are passed as values, and as an array, similarly to <see cref="call_user_func_array"/>. /// </summary> public static PhpValue forward_static_call_array(Context ctx, [ImportValue(ImportValueAttribute.ValueSpec.CallerStaticClass)] PhpTypeInfo @static, IPhpCallable function, PhpArray args) { return(forward_static_call(ctx, @static, function, args.GetValues())); }
internal ReflectionClass(PhpTypeInfo tinfo) { Debug.Assert(tinfo != null); _tinfo = tinfo; }
public virtual void __construct(Context ctx, PhpValue @class) { Debug.Assert(_tinfo == null, "Subsequent call not allowed."); _tinfo = ReflectionUtils.ResolvePhpTypeInfo(ctx, @class); }
/// <summary> /// Binds callback to given late static bound type. /// </summary> public virtual PhpCallable BindToStatic(Context ctx, PhpTypeInfo @static) => Bind(ctx);