///////////////////////////////////////////////////////////////////////////////// private static BindingRestrictions DeduceArgumentRestriction( int parameterIndex, ICSharpInvokeOrInvokeMemberBinder callPayload, DynamicMetaObject argument, CSharpArgumentInfo info) { // Here we deduce what predicates the DLR can apply to future calls in order to // determine whether to use the previously-computed-and-cached delegate, or // whether we need to bind the site again. Ideally we would like the // predicate to be as broad as is possible; if we can re-use analysis based // solely on the type of the argument, that is preferable to re-using analysis // based on object identity with a previously-analyzed argument. // The times when we need to restrict re-use to a particular instance, rather // than its type, are: // // * if the argument is a null reference then we have no type information. // // * if we are making a static call then the first argument is // going to be a Type object. In this scenario we should always check // for a specific Type object rather than restricting to the Type type. // // * if the argument was dynamic at compile time and it is a dynamic proxy // object that the runtime manages, such as COM RCWs and transparent // proxies. // // ** there is also a case for constant values (such as literals) to use // something like value restrictions, and that is accomplished in Bind(). bool useValueRestriction = argument.Value == null || IsTypeOfStaticCall(parameterIndex, callPayload) || IsDynamicallyTypedRuntimeProxy(argument, info); return(useValueRestriction ? BindingRestrictions.GetInstanceRestriction(argument.Expression, argument.Value) : BindingRestrictions.GetTypeRestriction(argument.Expression, argument.RuntimeType)); }
internal static DynamicMetaObject Bind( DynamicMetaObjectBinder action, RuntimeBinder binder, IEnumerable <DynamicMetaObject> args, IEnumerable <CSharpArgumentInfo> arginfos, DynamicMetaObject onBindingError) { List <Expression> parameters = new List <Expression>(); BindingRestrictions restrictions = BindingRestrictions.Empty; ICSharpInvokeOrInvokeMemberBinder callPayload = action as ICSharpInvokeOrInvokeMemberBinder; ParameterExpression tempForIncrement = null; IEnumerator <CSharpArgumentInfo> arginfosEnum = arginfos == null ? null : arginfos.GetEnumerator(); int index = 0; foreach (DynamicMetaObject o in args) { // Our contract with the DLR is such that we will not enter a bind unless we have // values for the meta-objects involved. if (!o.HasValue) { Debug.Assert(false, "The runtime binder is being asked to bind a metaobject without a value"); throw Error.InternalCompilerError(); } CSharpArgumentInfo info = null; if (arginfosEnum != null && arginfosEnum.MoveNext()) { info = arginfosEnum.Current; } if (index == 0 && IsIncrementOrDecrementActionOnLocal(action)) { // We have an inc or a dec operation. Insert the temp local instead. // // We need to do this because for value types, the object will come // in boxed, and we'd need to unbox it to get the original type in order // to increment. The only way to do that is to create a new temporary. tempForIncrement = Expression.Variable(o.Value != null ? o.Value.GetType() : typeof(object), "t0"); parameters.Add(tempForIncrement); } else { parameters.Add(o.Expression); } BindingRestrictions r = DeduceArgumentRestriction(index, callPayload, o, info); restrictions = restrictions.Merge(r); // Here we check the argument info. If the argument info shows that the current argument // is a literal constant, then we also add an instance restriction on the value of // the constant. if (info != null && info.LiteralConstant) { if ((o.Value is float && float.IsNaN((float)o.Value)) || o.Value is double && double.IsNaN((double)o.Value)) { // We cannot create an equality restriction for NaN, because equality is implemented // in such a way that NaN != NaN and the rule we make would be unsatisfiable. } else { Expression e = Expression.Equal(o.Expression, Expression.Constant(o.Value, o.Expression.Type)); r = BindingRestrictions.GetExpressionRestriction(e); restrictions = restrictions.Merge(r); } } ++index; } // Get the bound expression. try { DynamicMetaObject deferredBinding; Expression expression = binder.Bind(action, parameters, args.ToArray(), out deferredBinding); if (deferredBinding != null) { expression = ConvertResult(deferredBinding.Expression, action); restrictions = deferredBinding.Restrictions.Merge(restrictions); return(new DynamicMetaObject(expression, restrictions)); } if (tempForIncrement != null) { // If we have a ++ or -- payload, we need to do some temp rewriting. // We rewrite to the following: // // temp = (type)o; // temp++; // o = temp; // return o; DynamicMetaObject arg0 = Enumerable.First(args); Expression assignTemp = Expression.Assign( tempForIncrement, Expression.Convert(arg0.Expression, arg0.Value.GetType())); Expression assignResult = Expression.Assign( arg0.Expression, Expression.Convert(tempForIncrement, arg0.Expression.Type)); List <Expression> expressions = new List <Expression>(); expressions.Add(assignTemp); expressions.Add(expression); expressions.Add(assignResult); expression = Expression.Block(new ParameterExpression[] { tempForIncrement }, expressions); } expression = ConvertResult(expression, action); return(new DynamicMetaObject(expression, restrictions)); } catch (RuntimeBinderException e) { if (onBindingError != null) { return(onBindingError); } return(new DynamicMetaObject( Expression.Throw( Expression.New( typeof(RuntimeBinderException).GetConstructor(new Type[] { typeof(string) }), Expression.Constant(e.Message) ), GetTypeForErrorMetaObject(action, args.FirstOrDefault()) ), restrictions )); } }
///////////////////////////////////////////////////////////////////////////////// private static bool IsTypeOfStaticCall( int parameterIndex, ICSharpInvokeOrInvokeMemberBinder callPayload) { return(parameterIndex == 0 && callPayload != null && callPayload.StaticCall); }
internal static DynamicMetaObject Bind( ICSharpBinder action, RuntimeBinder binder, DynamicMetaObject[] args, IEnumerable <CSharpArgumentInfo> arginfos, DynamicMetaObject onBindingError) { Expression[] parameters = new Expression[args.Length]; BindingRestrictions restrictions = BindingRestrictions.Empty; ICSharpInvokeOrInvokeMemberBinder callPayload = action as ICSharpInvokeOrInvokeMemberBinder; ParameterExpression tempForIncrement = null; IEnumerator <CSharpArgumentInfo> arginfosEnum = (arginfos ?? Array.Empty <CSharpArgumentInfo>()).GetEnumerator(); for (int index = 0; index < args.Length; ++index) { DynamicMetaObject o = args[index]; // Our contract with the DLR is such that we will not enter a bind unless we have // values for the meta-objects involved. Debug.Assert(o.HasValue); CSharpArgumentInfo info = arginfosEnum.MoveNext() ? arginfosEnum.Current : null; if (index == 0 && IsIncrementOrDecrementActionOnLocal(action)) { // We have an inc or a dec operation. Insert the temp local instead. // // We need to do this because for value types, the object will come // in boxed, and we'd need to unbox it to get the original type in order // to increment. The only way to do that is to create a new temporary. object value = o.Value; tempForIncrement = Expression.Variable(value != null ? value.GetType() : typeof(object), "t0"); parameters[0] = tempForIncrement; } else { parameters[index] = o.Expression; } BindingRestrictions r = DeduceArgumentRestriction(index, callPayload, o, info); restrictions = restrictions.Merge(r); // Here we check the argument info. If the argument info shows that the current argument // is a literal constant, then we also add an instance restriction on the value of // the constant. if (info != null && info.LiteralConstant) { if (o.Value is double && double.IsNaN((double)o.Value)) { MethodInfo isNaN = s_DoubleIsNaN ?? (s_DoubleIsNaN = typeof(double).GetMethod("IsNaN")); Expression e = Expression.Call(null, isNaN, o.Expression); restrictions = restrictions.Merge(BindingRestrictions.GetExpressionRestriction(e)); } else if (o.Value is float && float.IsNaN((float)o.Value)) { MethodInfo isNaN = s_SingleIsNaN ?? (s_SingleIsNaN = typeof(float).GetMethod("IsNaN")); Expression e = Expression.Call(null, isNaN, o.Expression); restrictions = restrictions.Merge(BindingRestrictions.GetExpressionRestriction(e)); } else { Expression e = Expression.Equal(o.Expression, Expression.Constant(o.Value, o.Expression.Type)); r = BindingRestrictions.GetExpressionRestriction(e); restrictions = restrictions.Merge(r); } } } // Get the bound expression. try { Expression expression = binder.Bind(action, parameters, args, out DynamicMetaObject deferredBinding); if (deferredBinding != null) { expression = ConvertResult(deferredBinding.Expression, action); restrictions = deferredBinding.Restrictions.Merge(restrictions); return(new DynamicMetaObject(expression, restrictions)); } if (tempForIncrement != null) { // If we have a ++ or -- payload, we need to do some temp rewriting. // We rewrite to the following: // // temp = (type)o; // temp++; // o = temp; // return o; DynamicMetaObject arg0 = args[0]; expression = Expression.Block( new[] { tempForIncrement }, Expression.Assign(tempForIncrement, Expression.Convert(arg0.Expression, arg0.Value.GetType())), expression, Expression.Assign(arg0.Expression, Expression.Convert(tempForIncrement, arg0.Expression.Type))); } expression = ConvertResult(expression, action); return(new DynamicMetaObject(expression, restrictions)); } catch (RuntimeBinderException e) { if (onBindingError != null) { return(onBindingError); } return(new DynamicMetaObject( Expression.Throw( Expression.New( typeof(RuntimeBinderException).GetConstructor(new Type[] { typeof(string) }), Expression.Constant(e.Message) ), GetTypeForErrorMetaObject(action, args) ), restrictions )); } }
///////////////////////////////////////////////////////////////////////////////// private EXPR BindCall( ICSharpInvokeOrInvokeMemberBinder payload, EXPR callingObject, ArgumentObject[] arguments, Dictionary<int, LocalVariableSymbol> dictionary) { if (payload is InvokeBinder && !callingObject.type.isDelegateType()) { throw Error.BindInvokeFailedNonDelegate(); } EXPR pResult = null; int arity = payload.TypeArguments != null ? payload.TypeArguments.Count : 0; MemberLookup mem = new MemberLookup(); Debug.Assert(_bindingContext.ContextForMemberLookup() != null); SymWithType swt = _symbolTable.LookupMember( payload.Name, callingObject, _bindingContext.ContextForMemberLookup(), arity, mem, (payload.Flags & CSharpCallFlags.EventHookup) != 0, true); if (swt == null) { mem.ReportErrors(); Debug.Assert(false, "Why didn't member lookup report an error?"); } if (swt.Sym.getKind() != SYMKIND.SK_MethodSymbol) { Debug.Assert(false, "Unexpected type returned from lookup"); throw Error.InternalCompilerError(); } // At this point, we're set up to do binding. We need to do the following: // // 1) Create the EXPRLOCALs for the arguments, linking them to the local // variable symbols defined above. // 2) Create the EXPRMEMGRP for the call and the EXPRLOCAL for the object // of the call, and link the correct local variable symbol as above. // 3) Do overload resolution to get back an EXPRCALL. // // Our caller takes care of the rest. // First we need to check the sym that we got back. If we got back a static // method, then we may be in the situation where the user called the method // via a simple name call through the phantom overload. If thats the case, // then we want to sub in a type instead of the object. EXPRMEMGRP memGroup = CreateMemberGroupEXPR(payload.Name, payload.TypeArguments, callingObject, swt.Sym.getKind()); if ((payload.Flags & CSharpCallFlags.SimpleNameCall) != 0) { callingObject.flags |= EXPRFLAG.EXF_SIMPLENAME; } if ((payload.Flags & CSharpCallFlags.EventHookup) != 0) { mem = new MemberLookup(); SymWithType swtEvent = _symbolTable.LookupMember( payload.Name.Split('_')[1], callingObject, _bindingContext.ContextForMemberLookup(), arity, mem, (payload.Flags & CSharpCallFlags.EventHookup) != 0, true); if (swtEvent == null) { mem.ReportErrors(); Debug.Assert(false, "Why didn't member lookup report an error?"); } CType eventCType = null; if (swtEvent.Sym.getKind() == SYMKIND.SK_FieldSymbol) { eventCType = swtEvent.Field().GetType(); } else if (swtEvent.Sym.getKind() == SYMKIND.SK_EventSymbol) { eventCType = swtEvent.Event().type; } Type eventType = SymbolLoader.GetTypeManager().SubstType(eventCType, swtEvent.Ats).AssociatedSystemType; if (eventType != null) { // If we have an event hookup, first find the event itself. BindImplicitConversion(new ArgumentObject[] { arguments[1] }, eventType, dictionary, false); } memGroup.flags &= ~EXPRFLAG.EXF_USERCALLABLE; if (swtEvent.Sym.getKind() == SYMKIND.SK_EventSymbol && swtEvent.Event().IsWindowsRuntimeEvent) { return BindWinRTEventAccessor( new EventWithType(swtEvent.Event(), swtEvent.Ats), callingObject, arguments, dictionary, payload.Name.StartsWith("add_", StringComparison.Ordinal)); //isAddAccessor? } } // Check if we have a potential call to an indexed property accessor. // If so, we'll flag overload resolution to let us call non-callables. if ((payload.Name.StartsWith("set_", StringComparison.Ordinal) && swt.Sym.AsMethodSymbol().Params.Size > 1) || (payload.Name.StartsWith("get_", StringComparison.Ordinal) && swt.Sym.AsMethodSymbol().Params.Size > 0)) { memGroup.flags &= ~EXPRFLAG.EXF_USERCALLABLE; } pResult = _binder.BindMethodGroupToArguments(// Tree BindingFlag.BIND_RVALUEREQUIRED | BindingFlag.BIND_STMTEXPRONLY, memGroup, CreateArgumentListEXPR(arguments, dictionary, 1, arguments.Length)); // If overload resolution failed, throw an error. if (pResult == null || !pResult.isOK()) { throw Error.BindCallFailedOverloadResolution(); } CheckForConditionalMethodError(pResult); return ReorderArgumentsForNamedAndOptional(callingObject, pResult); }
///////////////////////////////////////////////////////////////////////////////// private EXPR CreateCallingObjectForCall( ICSharpInvokeOrInvokeMemberBinder payload, ArgumentObject[] arguments, Dictionary<int, LocalVariableSymbol> dictionary) { // Here we have a regular call, so create the calling object off of the first // parameter and pass it through. EXPR callingObject; if (payload.StaticCall) { if (arguments[0].Value == null || !(arguments[0].Value is Type)) { Debug.Assert(false, "Cannot make static call without specifying a type"); throw Error.InternalCompilerError(); } Type t = arguments[0].Value as Type; callingObject = _exprFactory.CreateClass(_symbolTable.GetCTypeFromType(t), null, t.GetTypeInfo().ContainsGenericParameters ? _exprFactory.CreateTypeArguments(SymbolLoader.getBSymmgr().AllocParams(_symbolTable.GetCTypeArrayFromTypes(t.GetGenericArguments())), null) : null); } else { // If we have a null argument, just bail and throw. if (!arguments[0].Info.UseCompileTimeType && arguments[0].Value == null) { throw Error.NullReferenceOnMemberException(); } callingObject = _binder.mustConvert( CreateArgumentEXPR(arguments[0], dictionary[0]), _symbolTable.GetCTypeFromType(arguments[0].Type)); if (arguments[0].Type.GetTypeInfo().IsValueType && callingObject.isCAST()) { // If we have a struct type, unbox it. callingObject.flags |= EXPRFLAG.EXF_UNBOXRUNTIME; } } return callingObject; }