_ParamInfo[] _GetParamInfos(CodeExpressionCollection parms, CodeDomResolverScope scope = null) { var result = new _ParamInfo[parms.Count]; for (var i = 0; i < result.Length; i++) { CodeExpression e = parms[i]; _ParamInfo p = default(_ParamInfo); p.IsOptional = false; p.IsRetval = false; var de = e as CodeDirectionExpression; if (null != de) { switch (de.Direction) { case FieldDirection.In: break; case FieldDirection.Out: p.IsOut = true; break; case FieldDirection.Ref: p.IsIn = p.IsOut = true; break; } e = de.Expression; } p.ParameterType = _resolver.GetTypeOfExpression(e, _scope); result[i] = p; } return(result); }
/// <summary> /// Attempts to return the type of the specified expression using the specified scope /// </summary> /// <param name="expr">The expression to evaluate</param> /// <param name="scope">The scope to use, or null to use the expression's current scope</param> /// <returns>A <see cref="CodeTypeReference"/> representing the type of the expression or null if it could not be retrieved</returns> public CodeTypeReference TryGetTypeOfExpression(CodeExpression expr, CodeDomResolverScope scope = null) { // reimplementing a try version of this is a lot of code, and frankly, the resolution takes so much // longer than any exception handling that this is acceptable in this case. try { return(GetTypeOfExpression(expr, scope)); } catch (Exception) { return(null); } }
Type _EvalType(CodeTypeReference r, CodeDomResolverScope s) { if (null == s) { s = GetScope(r); } var t = _ResolveType(r, s); if (null == t) { throw new TypeLoadException("The type could not be resolved"); } var result = t as Type; if (null == result) { throw new NotSupportedException("Only runtime types may be evaluated"); } return(result); }
_ParamInfo[] _GetParamInfos(CodeParameterDeclarationExpressionCollection parms, CodeDomResolverScope scope = null) { var result = new _ParamInfo[parms.Count]; for (var i = 0; i < result.Length; i++) { _ParamInfo p = default(_ParamInfo); p.IsOptional = false; p.IsRetval = false; p.Name = parms[i].Name; p.DefaultValue = DBNull.Value; var pd = parms[i]; switch (pd.Direction) { case FieldDirection.In: break; case FieldDirection.Out: p.IsOut = true; break; case FieldDirection.Ref: p.IsIn = p.IsOut = true; break; } p.ParameterType = pd.Type; if (null != scope) { p.ParameterType = _resolver.GetQualifiedType(pd.Type, scope); } result[i] = p; } return(result); }
/// <summary> /// Initializes the binder with the given scope /// </summary> /// <param name="scope">The scope in which the binder is to operate</param> public CodeDomBinder(CodeDomResolverScope scope) { _resolver = scope.Resolver; _scope = scope; }
/// <summary> /// Gets the type of the specified expression, at the optional given scope /// </summary> /// <param name="expr">The expression to evaluate</param> /// <param name="scope">The scope at which evaluation occurs, or null to use the expression's scope</param> /// <returns>A <see cref="CodeTypeReference"/> representing the type of the expression</returns> public CodeTypeReference GetTypeOfExpression(CodeExpression expr, CodeDomResolverScope scope = null) { if (null == expr) { throw new ArgumentNullException(nameof(expr)); } // first let's do the easy ones. var cpe = expr as CodePrimitiveExpression; if (null != cpe) { if (null == cpe.Value) { return(new CodeTypeReference(typeof(void))); } return(new CodeTypeReference(cpe.Value.GetType())); } var cbe = expr as CodeBinaryOperatorExpression; if (null != cbe) { switch (cbe.Operator) { case CodeBinaryOperatorType.BooleanAnd: case CodeBinaryOperatorType.BooleanOr: case CodeBinaryOperatorType.GreaterThan: case CodeBinaryOperatorType.GreaterThanOrEqual: case CodeBinaryOperatorType.IdentityEquality: case CodeBinaryOperatorType.IdentityInequality: case CodeBinaryOperatorType.LessThan: case CodeBinaryOperatorType.LessThanOrEqual: case CodeBinaryOperatorType.ValueEquality: return(new CodeTypeReference(typeof(bool))); case CodeBinaryOperatorType.Assign: case CodeBinaryOperatorType.Add: case CodeBinaryOperatorType.Subtract: case CodeBinaryOperatorType.Multiply: case CodeBinaryOperatorType.Divide: case CodeBinaryOperatorType.Modulus: case CodeBinaryOperatorType.BitwiseAnd: case CodeBinaryOperatorType.BitwiseOr: return(_PromoteType(GetTypeOfExpression(cbe.Left), GetTypeOfExpression(cbe.Right))); } } var tr = expr as CodeTypeReferenceExpression; if (null != tr) { if (null == tr.Type) { throw new InvalidOperationException("The type reference expression had no target object"); } return(tr.Type); } var pd = expr as CodeParameterDeclarationExpression; if (null != pd) { if (null == pd.Type) { throw new InvalidOperationException("The parameter declaration had no target object"); } return(pd.Type); } var oc = expr as CodeObjectCreateExpression; if (null != oc) { if (null == oc.CreateType) { throw new InvalidOperationException("The object creation expression had no create type"); } return(oc.CreateType); } var ac = expr as CodeArrayCreateExpression; if (null != ac) { if (null == ac.CreateType) { throw new InvalidOperationException("The array creation expression had no create type"); } var ctr = new CodeTypeReference(); ctr.ArrayElementType = ac.CreateType.ArrayElementType; ctr.ArrayRank = ac.CreateType.ArrayRank; ctr.BaseType = ac.CreateType.BaseType; ctr.TypeArguments.AddRange(ac.CreateType.TypeArguments); return(ctr); } var dc = expr as CodeDelegateCreateExpression; if (null != dc) { if (null == dc.DelegateType) { throw new InvalidOperationException("The delegate creation expression had no delegate type"); } return(dc.DelegateType); } var dv = expr as CodeDefaultValueExpression; if (null != dv) { if (null == dv.Type) { throw new InvalidOperationException("The default value expression had no type"); } return(dv.Type); } var dire = expr as CodeDirectionExpression; if (null != dire) { if (null == dire.Expression) { throw new InvalidOperationException("The direction expression had no target expression"); } return(GetTypeOfExpression(dire.Expression, scope)); } var ai = expr as CodeArrayIndexerExpression; if (null != ai) { var aet = GetTypeOfExpression(ai.TargetObject).ArrayElementType; if (null == aet) { throw new InvalidOperationException("The associated array type's array element type was null"); } return(aet); } var cst = expr as CodeCastExpression; if (null != cst) { if (null == cst.TargetType) { throw new InvalidOperationException("The cast expression's target type was null"); } return(cst.TargetType); } var to = expr as CodeTypeOfExpression; if (null != to) { return(new CodeTypeReference(typeof(Type))); } // now things get complicated if (null == scope) { scope = GetScope(expr); } var cmi = expr as CodeMethodInvokeExpression; if (null != cmi) { var types = new CodeTypeReference[cmi.Parameters.Count]; for (var i = 0; i < types.Length; ++i) { var p = cmi.Parameters[i]; var de = p as CodeDirectionExpression; if (null != de) { p = de.Expression; } types[i] = GetTypeOfExpression(p, scope); if (null == types[i]) { throw new InvalidOperationException(string.Format("Could not resolve parameter index {0} of method invoke expression", i)); } } var mr = cmi.Method; var t = GetTypeOfExpression(mr.TargetObject, scope); var rt = TryResolveType(t, scope); if (null == rt) { throw new InvalidOperationException("Could not resolve the type of the target expression of the method invoke expression"); } var rtt = rt as Type; object tm = null; var binder = new CodeDomBinder(scope); var grp = binder.GetMethodGroup(rt, mr.MethodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); //tm =binder.BindToMethod(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, grp,types, null,null,null,out state); tm = binder.SelectMethod(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, grp, types, null); if (null == tm) { throw new InvalidOperationException("Unable to find a suitable method to bind to in method invoke expression"); } var mi = tm as MethodInfo; if (null != mi) { return(new CodeTypeReference(mi.ReturnType)); } var cm = tm as CodeMemberMethod; if (null == cm.ReturnType) { return(new CodeTypeReference(typeof(void))); } return(cm.ReturnType); } var ar = expr as CodeArgumentReferenceExpression; if (null != ar) { var t = scope.ArgumentTypes[ar.ParameterName]; if (null == t) { throw new InvalidOperationException("The argument's type was null"); } return(t); } var vr = expr as CodeVariableReferenceExpression; if (null != vr) { var t = scope.VariableTypes[vr.VariableName]; if (null == t) { throw new InvalidOperationException("The variable's type was null. This could be due to an unresolved var declaration in Slang"); } return(t); } var br = expr as CodeBaseReferenceExpression; if (null != br) { var dt = scope.DeclaringType; if (null != dt) { if (0 < dt.BaseTypes.Count) { // this isn't exactly right. See notes below var bt = dt.BaseTypes[0]; if (null == bt) { throw new InvalidOperationException("The declaring type's base types contained a null entry."); } return(bt); } else if (dt.IsClass || dt.IsInterface) { return(new CodeTypeReference(typeof(object))); } else if (dt.IsEnum) { return(new CodeTypeReference(typeof(Enum))); } else if (dt.IsStruct) { return(new CodeTypeReference(typeof(ValueType))); } else { throw new InvalidOperationException("The declaring type is not a class, interface, enum or struct"); } } throw new InvalidOperationException("There is no declarting type in the scope from which to retrieve a base reference"); } var th = expr as CodeThisReferenceExpression; if (null != th) { var dt = scope.DeclaringType; if (null != dt) { // TODO: We have no way to fully resolve this because we'd need to know // what expression created this object. If it's a template we can't know // what the template args are. The best we can return is something like // Foo<> so we do that. This is hateful but I don't know what else to do // here return(new CodeTypeReference(_GetBaseNameOfType(dt, scope))); } throw new InvalidOperationException("There was no declaring type in the scope from which to retrieve a this reference"); } var fr = expr as CodeFieldReferenceExpression; if (null != fr) { var t = GetTypeOfExpression(fr.TargetObject, scope); var tt = _ResolveType(t, scope); if (null == tt) { throw new InvalidOperationException("The field reference's target expression type could not be resolved"); } var binder = new CodeDomBinder(scope); var fl = BindingFlags.Public | BindingFlags.NonPublic; var isStatic = (fr.TargetObject as CodeTypeReferenceExpression) != null; if (isStatic) { fl |= BindingFlags.Static; } else { fl |= BindingFlags.Instance; } var res = binder.GetField(tt, fr.FieldName, fl); if (null != res) { var mi = res as MemberInfo; if (null != mi) { return(GetTypeForMember(mi)); } return(GetTypeForMember(res as CodeTypeMember)); } throw new InvalidOperationException("A matching field could not be found"); } var pr = expr as CodePropertyReferenceExpression; if (null != pr) { var t = GetTypeOfExpression(pr.TargetObject, scope); var tt = _ResolveType(t, scope); if (null == tt) { throw new InvalidOperationException("The property reference's target expression type could not be resolved"); } var binder = new CodeDomBinder(scope); var fl = BindingFlags.Public | BindingFlags.NonPublic; var isStatic = (pr.TargetObject as CodeTypeReferenceExpression) != null; if (isStatic) { fl |= BindingFlags.Static; } else { fl |= BindingFlags.Instance; } var res = binder.GetPropertyGroup(tt, pr.PropertyName, fl); if (0 < res.Length) { var mi = res[0] as MemberInfo; if (null != mi) { return(GetTypeForMember(mi)); } return(GetTypeForMember(res[0] as CodeTypeMember)); } throw new InvalidOperationException("A matching property could not be found"); } var er = expr as CodeEventReferenceExpression; if (null != er) { var t = GetTypeOfExpression(er.TargetObject, scope); var tt = _ResolveType(t, scope); if (null == tt) { throw new InvalidOperationException("The event reference's target expression type could not be resolved"); } var binder = new CodeDomBinder(scope); var fl = BindingFlags.Public | BindingFlags.NonPublic; var isStatic = (er.TargetObject as CodeTypeReferenceExpression) != null; if (isStatic) { fl |= BindingFlags.Static; } else { fl |= BindingFlags.Instance; } var res = binder.GetEvent(tt, er.EventName, fl); if (null != res) { var mi = res as MemberInfo; if (null != mi) { return(GetTypeForMember(mi)); } else { return(GetTypeForMember(res as CodeTypeMember)); } } throw new InvalidOperationException("A matching event could not be found"); } var di = expr as CodeDelegateInvokeExpression; if (null != di) { var ctr = GetTypeOfExpression(di.TargetObject, scope); var tt = _ResolveType(ctr, scope) as Type; if (null == tt) { throw new InvalidOperationException("The delegate invoke expression's target expression type could not resolved."); } var ma = tt.GetMember("Invoke"); if (0 < ma.Length) { var mi = ma[0] as MethodInfo; if (null != mi) { return(new CodeTypeReference(mi.ReturnType)); } } throw new InvalidOperationException("The target is not a delegate"); } var ie = expr as CodeIndexerExpression; if (null != ie) { var t = GetTypeOfExpression(ie.TargetObject, scope); var types = new CodeTypeReference[ie.Indices.Count]; for (var i = 0; i < types.Length; ++i) { var p = ie.Indices[i]; var de = p as CodeDirectionExpression; if (null != de) { p = de.Expression; } types[i] = GetTypeOfExpression(p, scope); if (IsNullOrVoidType(types[i])) { throw new InvalidOperationException("One or more of the indexer argument types was void"); } } var tt = TryResolveType(t, scope); if (null == tt) { throw new InvalidOperationException("The indexer expression's target expression type could not be resolved"); } var binder = new CodeDomBinder(scope); var td = tt as CodeTypeDeclaration; object tm = null; if (null != td) { var grp = binder.GetPropertyGroup(td, "Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); tm = binder.SelectProperty(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, grp, null, types, null); } else { var rt = tt as Type; if (null != rt) { var grp = binder.GetPropertyGroup(rt, "Item", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); tm = binder.SelectProperty(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance, grp, null, types, null); } } if (null == tm) { throw new InvalidOperationException("The indexer expression's target object type does not have a matching indexer property"); } var pi = tm as PropertyInfo; if (null != pi) { return(new CodeTypeReference(pi.PropertyType)); } var cm = tm as CodeMemberProperty; if (null == cm.Type) { throw new InvalidOperationException("The property declaration's property type was null"); } return(cm.Type); } throw new InvalidOperationException(string.Format("Unsupported expression type {0}", expr.GetType().Name)); }
object _EvalBinOp(CodeBinaryOperatorExpression bo, CodeDomResolverScope s) { if (null == s) { s = GetScope(bo); } switch (bo.Operator) { case CodeBinaryOperatorType.Add: return(_Add(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.Subtract: return(_Subtract(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.Multiply: return(_Multiply(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.Divide: return(_Divide(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.Modulus: return(_Modulo(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.Assign: throw new NotSupportedException("Evaluate cannot change state."); case CodeBinaryOperatorType.BitwiseAnd: return(_BitwiseAnd(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.BitwiseOr: return(_BitwiseOr(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.BooleanAnd: return(((bool)_Eval(bo.Left, s)) && ((bool)_Eval(bo.Right, s))); case CodeBinaryOperatorType.BooleanOr: return(((bool)_Eval(bo.Left, s)) || ((bool)_Eval(bo.Right, s))); case CodeBinaryOperatorType.LessThan: return(_LessThan(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.LessThanOrEqual: return(_LessThanOrEqual(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.GreaterThan: return(_GreaterThan(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.GreaterThanOrEqual: return(_GreaterThanOrEqual(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.IdentityEquality: case CodeBinaryOperatorType.ValueEquality: return(_Equals(_Eval(bo.Left, s), _Eval(bo.Right, s))); case CodeBinaryOperatorType.IdentityInequality: return(_NotEqual(_Eval(bo.Left, s), _Eval(bo.Right, s))); default: throw new NotSupportedException("The specified operation is not supported."); } }
object _Eval(CodeExpression e, CodeDomResolverScope s) { var ac = e as CodeArrayCreateExpression; if (null != ac) { if (null == s) { s = GetScope(ac); } var type = _EvalType(ac.CreateType, s); var len = ac.Initializers.Count; if (0 == len) { if (0 < ac.Size) { len = ac.Size; } else { len = (int)_Eval(ac.SizeExpression, s); } } var arr = Array.CreateInstance(type, len); if (0 < ac.Initializers.Count) { for (int ic = ac.Initializers.Count, i = 0; i < ic; ++i) { arr.SetValue(_Eval(ac.Initializers[i], s), i); } } } var ai = e as CodeArrayIndexerExpression; if (null != ai) { if (null == s) { s = GetScope(e); } var arr = (Array)_Eval(ai.TargetObject, s); var ind = new int[ai.Indices.Count]; for (var i = 0; i < ind.Length; i++) { ind[i] = (int)_Eval(ai.Indices[i], s); } return(arr.GetValue(ind)); } var bo = e as CodeBinaryOperatorExpression; if (null != bo) { return(_EvalBinOp(bo, s)); } var c = e as CodeCastExpression; if (null != c) { if (null == s) { s = GetScope(c); } var type = _EvalType(c.TargetType, s); var rhs = _Eval(c.Expression, s); if (null == rhs) // cast from null allowed { if (type.IsValueType) { throw new InvalidCastException("Cannot cast null to a value type"); } return(null); } if (rhs.GetType().IsAssignableFrom(type)) { return(rhs); } throw new InvalidCastException("The value is not assignable to that target type"); } var dv = e as CodeDefaultValueExpression; if (null != dv) { if (null == s) { s = GetScope(c); } var type = _EvalType(c.TargetType, s); return(Activator.CreateInstance(type)); } var dc = e as CodeDelegateCreateExpression; if (null != dc) { if (null == s) { s = GetScope(dc); } var type = _EvalType(dc.DelegateType, s); var targ = _Eval(dc.TargetObject, s); var m = targ.GetType().GetMethod(dc.MethodName, ((BindingFlags)(-1)) & ~BindingFlags.DeclaredOnly); return(Delegate.CreateDelegate(type, targ, m)); } var di = e as CodeDelegateInvokeExpression; if (null != di) { if (null == s) { s = GetScope(di); } var lhs = _Eval(di.TargetObject, s); var parms = new object[di.Parameters.Count]; for (var i = 0; i < parms.Length; i++) { parms[i] = _Eval(di.Parameters[i], s); } var m = lhs.GetType().GetMethod("Invoke"); try { return(m.Invoke(lhs, parms)); } catch (TargetInvocationException tex) { throw tex.InnerException; } } var de = e as CodeDirectionExpression; if (null != de) { if (null == s) { s = GetScope(de); } return(_Eval(de.Expression, s)); } var er = e as CodeEventReferenceExpression; if (null != er) { if (null == s) { s = GetScope(er); } var ev = _Eval(er.TargetObject, s); var ei = ev.GetType().GetEvent(er.EventName); // i have no idea what else to return. A delegate? return(new KeyValuePair <EventInfo, object>(ei, ev)); } var fr = e as CodeFieldReferenceExpression; if (null != fr) { if (null == s) { s = GetScope(fr); } var trr = _Eval(fr.TargetObject, s); var type = trr as Type; if (null != type) { return(type.GetField(fr.FieldName).GetValue(null)); } return(trr.GetType().GetField(fr.FieldName).GetValue(trr)); } var ix = e as CodeIndexerExpression; if (null != ix) { if (null == s) { s = GetScope(ix); } var ir = _Eval(ix.TargetObject, s); var pia = _GetParamInfos(ix.Indices, s); var tt = ir as Type; var type = null != tt ? tt : ir.GetType(); try { if (null == tt) { return(type.InvokeMember("Item", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.InvokeMethod | BindingFlags.Instance, null, ir, _GetParamValues(pia))); } return(type.InvokeMember("Item", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, _GetParamValues(pia))); } catch (TargetInvocationException tex) { throw tex.InnerException; } } var mi = e as CodeMethodInvokeExpression; if (null != mi) { if (null == s) { s = GetScope(mi); } var mv = _Eval(mi.Method.TargetObject, s); var type = mv.GetType(); var tt = mv as Type; if (null != tt) { type = tt; } var pia = _GetParamInfos(mi.Parameters, s); try { if (null == tt) { return(type.InvokeMember(mi.Method.MethodName, BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, null, mv, _GetParamValues(pia))); } return(type.InvokeMember(mi.Method.MethodName, BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, _GetParamValues(pia))); } catch (TargetInvocationException tex) { throw tex.InnerException; } } var mr = e as CodeMethodReferenceExpression; if (null != mr) { if (null == s) { s = GetScope(mr); } var mv = _Eval(mr.TargetObject, s); var ml = new List <MethodInfo>(); var ma = mv.GetType().GetMethods(); for (var i = 0; i < ma.Length; ++i) { var m = ma[i]; if (0 == string.Compare(m.Name, mr.MethodName, StringComparison.InvariantCulture)) { ml.Add(m); } } return(new KeyValuePair <MethodInfo[], object>(ml.ToArray(), mv)); // basically returning a "MethodGroup" with an attached target object. stupid but what can we do? } var oc = e as CodeObjectCreateExpression; if (null != oc) { if (null == s) { s = GetScope(oc); } var t = _EvalType(oc.CreateType, s); var pia = _GetParamInfos(oc.Parameters, s); return(Activator.CreateInstance(t, _GetParamValues(pia))); } var p = e as CodePrimitiveExpression; if (null != p) { return(p.Value); } var pr = e as CodePropertyReferenceExpression; if (null != pr) { if (null == s) { s = GetScope(pr); } var trr = _Eval(pr.TargetObject, s); var type = trr as Type; if (null != type) { return(type.GetProperty(pr.PropertyName).GetValue(null)); } return(trr.GetType().GetProperty(pr.PropertyName).GetValue(trr)); } var to = e as CodeTypeOfExpression; if (null != to) { if (null == s) { s = GetScope(to); } return(_EvalType(to.Type, s)); } var tr = e as CodeTypeReferenceExpression; if (null != tr) { return(_EvalType(tr.Type, s)); } throw new NotSupportedException(string.Format("Unable to evaluate expressions of type {0}", e.GetType().FullName)); }
/// <summary> /// Evaluates the expression at the the given scope /// </summary> /// <param name="expression">The expression to evaluate</param> /// <param name="scope">The scope at which evaluation occurs or null to use the expression's scope</param> /// <returns>The result of the evaluation</returns> public object Evaluate(CodeExpression expression, CodeDomResolverScope scope = null) => _Eval(expression, scope);