/// <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)); }
static void _Patch(CodeFieldReferenceExpression fr, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != fr) { var path = _GetUnresRootPathOfExpression(fr); if (null != path) { // now we have something to work with. var scope = resolver.GetScope(fr); var sa = path.Split('.'); if (1 == sa.Length) { System.Diagnostics.Debugger.Break(); throw new NotImplementedException(); } else { object t = null; string tn = null; CodeExpression tf = fr; CodeExpression ptf = null; CodeTypeReference ctr = null; for (var i = sa.Length - 1; i >= 1; --i) { tn = string.Join(".", sa, 0, i); ptf = tf; tf = _GetTargetOfExpression(tf); ctr = new CodeTypeReference(tn); t = resolver.TryResolveType(ctr, scope); if (null != t) { break; } } if (null != t) { var tt = t as Type; if (null != tt) { ctr = new CodeTypeReference(tt); } else { ctr = resolver.GetQualifiedType(ctr, scope); } // we found a type reference _SetTargetOfExpression(ptf, new CodeTypeReferenceExpression(ctr)); return; //args.Cancel = true; } } } // this probably means part of our field has been resolved, or at the very least // it does not come from a rooted var ref. if (!fr.TargetObject.UserData.Contains("slang:unresolved")) { var scope = resolver.GetScope(fr); var binder = new CodeDomBinder(scope); var t = resolver.GetTypeOfExpression(fr.TargetObject); var isStatic = false; var tre = fr.TargetObject as CodeTypeReferenceExpression; if (null != tre) { isStatic = true; } var tt = resolver.TryResolveType(isStatic ? tre.Type: t, scope); if (null == tt) { throw new InvalidOperationException(string.Format("The type {0} could not be resolved.", t.BaseType)); } var td = tt as CodeTypeDeclaration; // TODO: This code could be a lot faster if we added some functionality to the binder // we're just checking to see if the method, property or field exists var m = binder.GetField(tt, fr.FieldName, _BindFlags); if (null != m) { fr.UserData.Remove("slang:unresolved"); return; } m = binder.GetEvent(tt, fr.FieldName, _BindFlags); if (null != m) { var er = new CodeEventReferenceExpression(fr.TargetObject, fr.FieldName); CodeDomVisitor.ReplaceTarget(ctx, er); return; } var ml = binder.GetMethodGroup(tt, fr.FieldName, _BindFlags); if (0 < ml.Length) { var mr = new CodeMethodReferenceExpression(fr.TargetObject, fr.FieldName); CodeDomVisitor.ReplaceTarget(ctx, mr); return; } ml = binder.GetPropertyGroup(tt, fr.FieldName, _BindFlags); if (0 < ml.Length) { var pr = new CodePropertyReferenceExpression(fr.TargetObject, fr.FieldName); CodeDomVisitor.ReplaceTarget(ctx, pr); return; } throw new InvalidProgramException(string.Format("Cannot deterimine the target reference {0}", fr.FieldName)); } } }