static Expression BindArrayAccess(Expression arr, Expression key, Expression ctx, AccessFlags access, Expression rvalue) { Debug.Assert(key.Type == typeof(IntStringKey)); if (access.EnsureObject()) { // (arr ?? arr = []).EnsureItemObject(key) return(Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemObject, key)); } else if (access.EnsureArray()) { // (arr ?? arr = []).EnsureItemArray(key) return(Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemArray, key)); } else if (access.EnsureAlias()) { // (arr ?? arr = []).EnsureItemAlias(key) return(Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemAlias, key)); } else if (access.WriteAlias()) { Debug.Assert(rvalue.Type == typeof(PhpAlias)); rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx); // (arr ?? arr = []).SetItemAlias(key, value) return(Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_SetItemAlias, key, rvalue)); } else if (access.Unset()) { Debug.Assert(rvalue == null); // remove key // arr.RemoveKey(name) // TODO: if (arr != null) return(Expression.Call(arr, Cache.Operators.PhpArray_RemoveKey, key)); } else if (access.Write()) { rvalue = ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx); return(Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_SetItemValue, key, rvalue)); } else { // read // TODO: (arr != null) ? arr[key] : (quiet ? void : ERROR) return(Expression.Call(arr, Cache.Operators.PhpArray_GetItemValue, key)); } }
/// <summary> /// Binds recursion check for property magic method. /// </summary> static Expression InvokeHandler(Expression ctx, Expression target, string field, Expression getter, AccessFlags access, Expression @default = null, Type resultType = null) { // default resultType = resultType ?? Cache.Types.PhpValue[0]; @default = @default ?? Expression.Field(null, Cache.Properties.PhpValue_Null); // TODO: ERR field not found @default = ConvertExpression.Bind(@default, resultType, ctx); if (getter == null) { return(@default); } else { /* Template: * var token; * try { * return (token = new Context.RecursionCheckToken(_ctx, target, access))).IsInRecursion) * ? default * : getter; * } finally { * token.Dispose(); * } */ int subkey = access.Write() ? 1 : access.Unset() ? 2 : access.Isset() ? 3 : 4; // recursion prevention scope // Template: RecursionCheckToken token; var tokenvar = Expression.Variable(typeof(Context.RecursionCheckToken), "token"); // Template: token = new RecursionCheckToken(_ctx, (object)target, (int)access)) var tokenassign = Expression.Assign(tokenvar, Expression.New(Cache.RecursionCheckToken.ctor_ctx_object_int, ctx, Expression.Convert(target, Cache.Types.Object[0]), Expression.Constant(subkey))); // return(Expression.Block(resultType, new[] { tokenvar }, Expression.TryFinally( Expression.Condition(Expression.Property(tokenassign, Cache.RecursionCheckToken.IsInRecursion), @default, ConvertExpression.Bind(getter, resultType, ctx)), Expression.Call(tokenvar, Cache.RecursionCheckToken.Dispose) ))); } }
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)); } } // // 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) ? result : (__isset(key) ?? null) result = Expression.Condition(trygetfield, resultvar, InvokeHandler(ctx, target, field, __isset, access)); } 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); }
static Expression BindAccess(Expression expr, Expression ctx, AccessFlags access, Expression rvalue) { if (access.EnsureObject()) { if (expr.Type == typeof(PhpAlias)) { // ((PhpAlias)fld).EnsureObject() expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureObject); } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureObject() expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureObject); } else { // getter // TODO: ensure it is not null Debug.Assert(!expr.Type.GetTypeInfo().IsValueType); } } else if (access.EnsureArray()) { if (expr.Type == typeof(PhpAlias)) { // ((PhpAlias)fld).EnsureArray() expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureArray); } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureArray() expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureArray); } else if (expr.Type == typeof(PhpArray)) { // (PhpArray)fld // TODO: ensure it is not null } else { // getter } } else if (access.EnsureAlias()) { if (expr.Type == typeof(PhpAlias)) { // (PhpAlias)getter } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureAlias() expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureAlias); } else { // getter // cannot read as reference } } else if (access.WriteAlias()) { // write alias Debug.Assert(rvalue.Type == typeof(PhpAlias)); rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx); if (expr.Type == typeof(PhpAlias)) { // ok } else if (expr.Type == typeof(PhpValue)) { // fld = PhpValue.Create(alias) rvalue = Expression.Call(typeof(PhpValue).GetMethod("Create", Cache.Types.PhpAlias), rvalue); } else { // fld is not aliasable Debug.Assert(false, "Cannot assign aliased value to field of type " + expr.Type.ToString()); rvalue = ConvertExpression.Bind(rvalue, expr.Type, ctx); } expr = Expression.Assign(expr, rvalue); } else if (access.Unset()) { Debug.Assert(rvalue == null); expr = Expression.Assign(expr, ConvertExpression.BindDefault(expr.Type)); } else if (access.Write()) { // write by value if (expr.Type == typeof(PhpAlias)) { // Template: fld.Value = (PhpValue)value expr = Expression.Assign(Expression.PropertyOrField(expr, "Value"), ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx)); } else if (expr.Type == typeof(PhpValue)) { // Template: Operators.SetValue(ref fld, (PhpValue)value) expr = Expression.Call(Cache.Operators.SetValue_PhpValueRef_PhpValue, expr, ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx)); } else { // Template: fld = value // default behaviour by value to value expr = Expression.Assign(expr, ConvertExpression.Bind(rvalue, expr.Type, ctx)); } } // return(expr); }
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; }
static Expression BindArrayAccess(Expression arr, Expression key, Expression ctx, AccessFlags access, Expression rvalue) { Debug.Assert(key.Type == typeof(IntStringKey)); if (access.EnsureObject()) { // (arr ?? arr = []).EnsureItemObject(key) return Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemObject, key); } else if (access.EnsureArray()) { // (arr ?? arr = []).EnsureItemArray(key) return Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemArray, key); } else if (access.EnsureAlias()) { // (arr ?? arr = []).EnsureItemAlias(key) return Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_EnsureItemAlias, key); } else if (access.WriteAlias()) { Debug.Assert(rvalue.Type == typeof(PhpAlias)); rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx); // (arr ?? arr = []).SetItemAlias(key, value) return Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_SetItemAlias, key, rvalue); } else if (access.Unset()) { Debug.Assert(rvalue == null); // remove key // arr.RemoveKey(name) // TODO: if (arr != null) return Expression.Call(arr, Cache.Operators.PhpArray_RemoveKey, key); } else if (access.Write()) { rvalue = ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx); return Expression.Call( EnsureNotNullPhpArray(arr), Cache.Operators.PhpArray_SetItemValue, key, rvalue); } else { // read // TODO: (arr != null) ? arr[key] : (quiet ? void : ERROR) return Expression.Call(arr, Cache.Operators.PhpArray_GetItemValue, key); } }
static Expression BindAccess(Expression expr, Expression ctx, AccessFlags access, Expression rvalue) { if (access.EnsureObject()) { if (expr.Type == typeof(PhpAlias)) { // ((PhpAlias)fld).EnsureObject(ctx) expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureObject_Context); } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureObject(ctx) expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureObject_Context); } else { // getter // TODO: ensure it is not null } } else if (access.EnsureArray()) { if (expr.Type == typeof(PhpAlias)) { // ((PhpAlias)fld).EnsureArray() expr = Expression.Call(expr, Cache.Operators.PhpAlias_EnsureArray); } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureArray() expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureArray); } else if (expr.Type == typeof(PhpArray)) { // (PhpArray)fld // TODO: ensure it is not null } else { // getter } } else if (access.EnsureAlias()) { if (expr.Type == typeof(PhpAlias)) { // (PhpAlias)getter } else if (expr.Type == typeof(PhpValue)) { // ((PhpValue)fld).EnsureAlias() expr = Expression.Call(expr, Cache.Operators.PhpValue_EnsureAlias); } else { // getter // cannot read as reference } } else if (access.WriteAlias()) { // write alias Debug.Assert(rvalue.Type == typeof(PhpAlias)); rvalue = ConvertExpression.Bind(rvalue, typeof(PhpAlias), ctx); if (expr.Type == typeof(PhpAlias)) { // ok } else if (expr.Type == typeof(PhpValue)) { // fld = PhpValue.Create(alias) rvalue = Expression.Call(typeof(PhpValue).GetMethod("Create", Cache.Types.PhpAlias), rvalue); } else { // fld is not aliasable Debug.Assert(false, "Cannot assign aliased value to field of type " + expr.Type.ToString()); rvalue = ConvertExpression.Bind(rvalue, expr.Type, ctx); } expr = Expression.Assign(expr, rvalue); } else if (access.Unset()) { Debug.Assert(rvalue == null); expr = Expression.Assign(expr, ConvertExpression.BindDefault(expr.Type)); } else if (access.Write()) { // write by value if (expr.Type == typeof(PhpAlias)) { // Template: fld.Value = (PhpValue)value expr = Expression.Assign(Expression.PropertyOrField(expr, "Value"), ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx)); } else if (expr.Type == typeof(PhpValue)) { // Template: Operators.SetValue(ref fld, (PhpValue)value) expr = Expression.Call(Cache.Operators.SetValue_PhpValueRef_PhpValue, expr, ConvertExpression.Bind(rvalue, typeof(PhpValue), ctx)); } else { // Template: fld = value // default behaviour by value to value expr = Expression.Assign(expr, ConvertExpression.Bind(rvalue, expr.Type, ctx)); } } // return expr; }
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); }