private Expression/*!*/ MaybeMakeNoThrow(GetBindingInfo/*!*/ info, Expression/*!*/ expr, Expression codeContext) { if (BindingHelpers.IsNoThrow(info.Action)) { DynamicMetaObject fallback = FallbackGetError(info.Action, codeContext); Type t = BindingHelpers.GetCompatibleType(expr.Type, fallback.Expression.Type); ParameterExpression tmp = Ast.Variable(t, "getAttrRes"); expr = Ast.Block( new ParameterExpression[] { tmp }, Ast.Block( AstUtils.Try( Ast.Assign(tmp, AstUtils.Convert(expr, t)) ).Catch( typeof(MissingMemberException), Ast.Assign(tmp, AstUtils.Convert(FallbackGetError(info.Action, codeContext).Expression, t)) ), tmp ) ); } return expr; }
/// <summary> /// Makes a rule which calls a user-defined __getattribute__ function and falls back to __getattr__ if that /// raises an AttributeError. /// /// slot is the __getattribute__ method to be called. /// </summary> private DynamicMetaObject/*!*/ MakeGetAttributeRule(GetBindingInfo/*!*/ info, IPythonObject/*!*/ obj, PythonTypeSlot/*!*/ slot, Expression codeContext) { // if the type implements IDynamicObject and we picked up it's __getattribute__ then we want to just // dispatch to the base meta object (or to the default binder). an example of this is: // // class mc(type): // def __getattr__(self, name): // return 42 // // class nc_ga(object): // __metaclass__ = mc // // a = nc_ga.x # here we want to dispatch to the type's rule, not call __getattribute__ directly. CodeContext context = BinderState.GetBinderState(info.Action).Context; Type finalType = PythonTypeOps.GetFinalSystemType(obj.PythonType.UnderlyingSystemType); if (typeof(IDynamicObject).IsAssignableFrom(finalType)) { PythonTypeSlot baseSlot; if (TryGetGetAttribute(context, DynamicHelpers.GetPythonTypeFromType(finalType), out baseSlot) && baseSlot == slot) { return Fallback(info.Action, codeContext); } } // otherwise generate code into a helper function. This will do the slot lookup and exception // handling for both __getattribute__ as well as __getattr__ if it exists. PythonTypeSlot getattr; obj.PythonType.TryResolveSlot(context, Symbols.GetBoundAttr, out getattr); DynamicMetaObject self = Restrict(Value.GetType()); string methodName = BindingHelpers.IsNoThrow(info.Action) ? "GetAttributeNoThrow" : "GetAttribute"; return BindingHelpers.AddDynamicTestAndDefer( info.Action, new DynamicMetaObject( Ast.Call( typeof(UserTypeOps).GetMethod(methodName), Ast.Constant(BinderState.GetBinderState(info.Action).Context), info.Args[0].Expression, Ast.Constant(GetGetMemberName(info.Action)), Ast.Constant(slot, typeof(PythonTypeSlot)), Ast.Constant(getattr, typeof(PythonTypeSlot)), Ast.Constant(new SiteLocalStorage<CallSite<Func<CallSite, CodeContext, object, string, object>>>()) ), self.Restrictions ), info.Args, info.Validation ); }
private static MethodCallExpression/*!*/ MakeGetAttrTestAndGet(GetBindingInfo/*!*/ info, Expression/*!*/ getattr) { return Ast.Call( TypeInfo._PythonOps.SlotTryGetBoundValue, Ast.Constant(BinderState.GetBinderState(info.Action).Context), AstUtils.Convert(getattr, typeof(PythonTypeSlot)), AstUtils.Convert(info.Self, typeof(object)), Ast.Convert( Ast.Property( Ast.Convert( info.Self, typeof(IPythonObject)), TypeInfo._IPythonObject.PythonType ), typeof(PythonType) ), info.Result ); }
private Expression/*!*/ MakeGetAttrCall(GetBindingInfo/*!*/ info, Expression codeContext) { Expression call = Ast.Dynamic( new PythonInvokeBinder( BinderState.GetBinderState(info.Action), new CallSignature(1) ), typeof(object), BinderState.GetCodeContext(info.Action), info.Result, Ast.Constant(GetGetMemberName(info.Action)) ); call = MaybeMakeNoThrow(info, call, codeContext); return call; }
private void MakeGetAttrRule(GetBindingInfo/*!*/ info, Expression/*!*/ getattr, Expression codeContext) { info.Body.AddCondition( MakeGetAttrTestAndGet(info, getattr), MakeGetAttrCall(info, codeContext) ); }
/// <summary> /// Checks a range of the MRO to perform old-style class lookups if any old-style classes /// are present. We will call this twice to produce a search before a slot and after /// a slot. /// </summary> private void MakeOldStyleAccess(GetBindingInfo/*!*/ info) { info.Body.AddCondition( Ast.Call( typeof(UserTypeOps).GetMethod("TryGetMixedNewStyleOldStyleSlot"), Ast.Constant(BinderState.GetBinderState(info.Action).Context), AstUtils.Convert(info.Self, typeof(object)), AstUtils.Constant(SymbolTable.StringToId(GetGetMemberName(info.Action))), info.Result ), info.Result ); }
private static void MakeSlotAccess(GetBindingInfo/*!*/ info, PythonTypeSlot dts) { ReflectedSlotProperty rsp = dts as ReflectedSlotProperty; if (rsp != null) { // we need to fall back to __getattr__ if the value is not defined, so call it and check the result. info.Body.AddCondition( Ast.NotEqual( Ast.Assign( info.Result, Ast.ArrayAccess( Ast.Call( Ast.Convert(info.Self, typeof(IObjectWithSlots)), typeof(IObjectWithSlots).GetMethod("GetSlots") ), Ast.Constant(rsp.Index) ) ), Ast.Field(null, typeof(Uninitialized).GetField("Instance")) ), info.Result ); return; } PythonTypeUserDescriptorSlot slot = dts as PythonTypeUserDescriptorSlot; if (slot != null && !(slot.Value is PythonTypeSlot)) { PythonType slottype = DynamicHelpers.GetPythonType(slot.Value); if (slottype.IsSystemType) { // this is a user slot that's known not to be a descriptor // so we can just burn the value in. For it to change the // slot will need to be replaced reving the type version. info.Body.FinishCondition( AstUtils.Convert(AstUtils.WeakConstant(slot.Value), typeof(object)) ); return; } } // users can subclass PythonProperty so check the type explicitly // and only in-line the ones we fully understand. if (dts.GetType() == typeof(PythonProperty)) { // properties are mutable so we generate code to get the value rather // than burning it into the rule. Expression getter = Ast.Property( Ast.Convert(AstUtils.WeakConstant(dts), typeof(PythonProperty)), "fget" ); ParameterExpression tmpGetter = Ast.Variable(typeof(object), "tmpGet"); info.Body.AddVariable(tmpGetter); info.Body.FinishCondition( Ast.Block( Ast.Assign(tmpGetter, getter), Ast.Condition( Ast.NotEqual( tmpGetter, Ast.Constant(null) ), Ast.Dynamic( new PythonInvokeBinder( BinderState.GetBinderState(info.Action), new CallSignature(1) ), typeof(object), Ast.Constant(BinderState.GetBinderState(info.Action).Context), tmpGetter, info.Self ), Ast.Throw(Ast.Call(typeof(PythonOps).GetMethod("UnreadableProperty")), typeof(object)) ) ) ); return; } Expression tryGet = Ast.Call( TypeInfo._PythonOps.SlotTryGetBoundValue, Ast.Constant(BinderState.GetBinderState(info.Action).Context), Ast.Convert(AstUtils.WeakConstant(dts), typeof(PythonTypeSlot)), AstUtils.Convert(info.Self, typeof(object)), Ast.Property( Ast.Convert( info.Self, typeof(IPythonObject)), TypeInfo._IPythonObject.PythonType ), info.Result ); if (dts.GetAlwaysSucceeds) { info.Body.FinishCondition( Ast.Block(tryGet, info.Result) ); } else { info.Body.AddCondition( tryGet, info.Result ); } }
private void MakeDictionaryAccess(GetBindingInfo/*!*/ info) { info.Body.AddCondition( Ast.AndAlso( Ast.NotEqual( Ast.Property( Ast.Convert(info.Self, typeof(IPythonObject)), TypeInfo._IPythonObject.Dict ), Ast.Constant(null) ), Ast.Call( Ast.Property( Ast.Convert(info.Self, typeof(IPythonObject)), TypeInfo._IPythonObject.Dict ), TypeInfo._IAttributesCollection.TryGetvalue, AstUtils.Constant(SymbolTable.StringToId(GetGetMemberName(info.Action))), info.Result ) ), info.Result ); }
private DynamicMetaObject GetMemberWorker(DynamicMetaObjectBinder/*!*/ member, Expression codeContext) { DynamicMetaObject self = Restrict(Value.GetType()); CodeContext context = BinderState.GetBinderState(member).Context; IPythonObject sdo = Value; GetBindingInfo bindingInfo = new GetBindingInfo( member, new DynamicMetaObject[] { this }, Ast.Variable(Expression.Type, "self"), Ast.Variable(typeof(object), "lookupRes"), new ConditionalBuilder(member), BindingHelpers.GetValidationInfo(self.Expression, sdo.PythonType) ); PythonTypeSlot foundSlot; if (TryGetGetAttribute(context, sdo.PythonType, out foundSlot)) { return MakeGetAttributeRule(bindingInfo, sdo, foundSlot, codeContext); } // otherwise look the object according to Python rules: // 1. 1st search the MRO of the type, and if it's there, and it's a get/set descriptor, // return that value. // 2. Look in the instance dictionary. If it's there return that value, otherwise return // a value found during the MRO search. If no value was found during the MRO search then // raise an exception. // 3. fall back to __getattr__ if defined. // // Ultimately we cache the result of the MRO search based upon the type version. If we have // a get/set descriptor we'll only ever use that directly. Otherwise if we have a get descriptor // we'll first check the dictionary and then invoke the get descriptor. If we have no descriptor // at all we'll just check the dictionary. If both lookups fail we'll raise an exception. bool isOldStyle; bool systemTypeResolution; foundSlot = FindSlot(context, GetGetMemberName(member), sdo, out isOldStyle, out systemTypeResolution); if (!isOldStyle || foundSlot is ReflectedSlotProperty) { if (sdo.HasDictionary && (foundSlot == null || !foundSlot.IsSetDescriptor(context, sdo.PythonType))) { MakeDictionaryAccess(bindingInfo); } if (foundSlot != null) { if (systemTypeResolution) { bindingInfo.Body.FinishCondition(GetMemberFallback(member, codeContext).Expression); } else { MakeSlotAccess(bindingInfo, foundSlot); } } } else { MakeOldStyleAccess(bindingInfo); } if (!bindingInfo.Body.IsFinal) { // fall back to __getattr__ if it's defined. PythonTypeSlot getattr; if (sdo.PythonType.TryResolveSlot(context, Symbols.GetBoundAttr, out getattr)) { MakeGetAttrRule(bindingInfo, GetWeakSlot(getattr), codeContext); } bindingInfo.Body.FinishCondition(FallbackGetError(member, null).Expression); } DynamicMetaObject res = bindingInfo.Body.GetMetaObject(this); res = new DynamicMetaObject( Ast.Block( new ParameterExpression[] { bindingInfo.Self, bindingInfo.Result }, Ast.Assign(bindingInfo.Self, self.Expression), res.Expression ), self.Restrictions.Merge(res.Restrictions) ); return BindingHelpers.AddDynamicTestAndDefer( member, res, new DynamicMetaObject[] { this }, bindingInfo.Validation ); }