static void _Patch(CodeDelegateInvokeExpression di, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != di) { // these can be method invokes depending. if (null != di.TargetObject) { // probably already fixed in an earlier visit var mr = di.TargetObject as CodeMethodReferenceExpression; if (null != mr) { var mi = new CodeMethodInvokeExpression(mr); mi.Parameters.AddRange(di.Parameters); CodeDomVisitor.ReplaceTarget(ctx, mi); //args.Cancel = true; } else { var cco = di.TargetObject as CodeObject; if (null == cco) { System.Diagnostics.Debugger.Break(); } } } else { // we really are at a loss here as the only way this would be valid is // through a self call on a delegate object itself, like this(); throw new InvalidProgramException("Untargeted delegate invoke produced by slang parser!"); } //return; } }
static void _Patch(CodeMemberMethod meth, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != meth) { // TODO: Populate public implementation types meth.UserData.Remove("slang:unresolved"); if ("Main" == meth.Name && (meth.Attributes & MemberAttributes.ScopeMask) == MemberAttributes.Static) { if (0 == meth.Parameters.Count && null == meth.ReturnType || "System.Void" == meth.ReturnType.BaseType) { var epm = new CodeEntryPointMethod(); epm.Attributes = meth.Attributes; epm.LinePragma = meth.LinePragma; epm.StartDirectives.AddRange(meth.StartDirectives); epm.EndDirectives.AddRange(meth.EndDirectives); epm.Comments.AddRange(meth.Comments); epm.CustomAttributes.AddRange(meth.CustomAttributes); epm.ReturnTypeCustomAttributes.AddRange(meth.ReturnTypeCustomAttributes); epm.TypeParameters.AddRange(meth.TypeParameters); epm.PrivateImplementationType = meth.PrivateImplementationType; epm.ImplementationTypes.AddRange(meth.ImplementationTypes); epm.Name = meth.Name; epm.Statements.AddRange(meth.Statements); CodeDomVisitor.ReplaceTarget(ctx, epm); } } //return; } }
static void _Patch(CodeMemberProperty prop, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != prop) { // TODO: add public implementation types prop.UserData.Remove("slang:unresolved"); } }
static bool _IsDelegate(CodeExpression target, CodeDomResolver res) { var v = target as CodeVariableReferenceExpression; if (null != v && v.UserData.Contains("slang:unresolved")) { var scope = res.GetScope(target); if (scope.MemberNames.Contains(v.VariableName)) { return(true); } } return(false); }
static void _Patch(CodeTypeReference tr, CodeDomVisitContext ctx, CodeDomResolver res) { if (null != tr) { if (res.IsValidType(tr, res.GetScope(tr))) { tr.UserData.Remove("slang:unresolved"); return; } // this is probably a nested type but with . instead of + // so now we need to crack it apart and hunt it down throw new NotImplementedException(); } }
static void _Patch(CodeObjectCreateExpression oc, CodeDomVisitContext ctx, CodeDomResolver res) { if (null != oc) // we have to check to see if this is a delegate creation expression { oc.UserData.Remove("slang:unresolved"); if (1 == oc.Parameters.Count) { if (_IsDelegate(oc.Parameters[0], res)) { var del = _GetDelegateFromFields(oc, oc.Parameters[0], res); CodeDomVisitor.ReplaceTarget(ctx, del); } } } }
static void _Patch(CodeIndexerExpression indexer, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != indexer) { if (indexer.TargetObject.UserData.Contains("slang:unresolved")) { return; } var ctr = resolver.GetTypeOfExpression(indexer.TargetObject); if (null != ctr.ArrayElementType && 0 < ctr.ArrayRank) { var ai = new CodeArrayIndexerExpression(indexer.TargetObject); ai.Indices.AddRange(indexer.Indices); CodeDomVisitor.ReplaceTarget(ctx, ai); //return; } indexer.UserData.Remove("slang:unresolved"); } }
/// <summary> /// Patches the CodeDOM tree received from the <see cref="SlangParser"/> into something more usable, by resolving type information and replacing various elements in the CodeDOM graph /// </summary> /// <param name="compileUnits">The <see cref="CodeCompileUnit"/> objects to patch</param> public static void Patch(IEnumerable <CodeCompileUnit> compileUnits) { var resolver = new CodeDomResolver(); foreach (var ccu in compileUnits) { resolver.CompileUnits.Add(ccu); } resolver.Refresh(); var working = -1; var oworking = 0; while (0 != working && oworking != working) { oworking = working; working = 0; for (int ic = resolver.CompileUnits.Count, i = 0; i < ic; ++i) { CodeDomVisitor.Visit(resolver.CompileUnits[i], (ctx) => { var co = ctx.Target as CodeObject; if (null != co && co.UserData.Contains("slang:unresolved")) { ++working; _Patch(ctx.Target as CodeFieldReferenceExpression, ctx, resolver); _Patch(ctx.Target as CodeVariableDeclarationStatement, ctx, resolver); _Patch(ctx.Target as CodeVariableReferenceExpression, ctx, resolver); _Patch(ctx.Target as CodeDelegateInvokeExpression, ctx, resolver); _Patch(ctx.Target as CodeObjectCreateExpression, ctx, resolver); _Patch(ctx.Target as CodeIndexerExpression, ctx, resolver); _Patch(ctx.Target as CodeMemberMethod, ctx, resolver); _Patch(ctx.Target as CodeMemberProperty, ctx, resolver); _Patch(ctx.Target as CodeTypeReference, ctx, resolver); } }); } resolver.Refresh(); } }
static void _Patch(CodeVariableDeclarationStatement vd, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != vd) { if (CodeDomResolver.IsNullOrVoidType(vd.Type)) { if (null == vd.InitExpression) { throw new ArgumentException("The code contains an incomplete variable declaration.", "resolver"); } if (!_HasUnresolved(vd.InitExpression)) { var t = resolver.GetTypeOfExpression(vd.InitExpression, resolver.GetScope(vd.InitExpression)); vd.Type = t; if (!CodeDomResolver.IsNullOrVoidType(t)) { vd.UserData.Remove("slang:unresolved"); } } } } }
/// <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; }
// Given a set of properties that match the base criteria, select one. /// <summary> /// Selects the property that matches the given signature /// </summary> /// <param name="flags">The binding flags to use</param> /// <param name="match">The properties to evaluate</param> /// <param name="returnType">The return type to evaluate or null to ignore</param> /// <param name="indices">The indices to compare with the signature</param> /// <param name="modifiers">Not used</param> /// <returns>The property that matches the signature, or null if none could be found</returns> public CodeMemberProperty SelectProperty(BindingFlags flags, CodeMemberProperty[] match, CodeTypeReference returnType, CodeTypeReference[] indices, ParameterModifier[] modifiers) { // Allow a null indexes array. But if it is not null, every element must be non-null as well. if (indices != null && !Contract.ForAll(indices, delegate(CodeTypeReference t) { return(t != null && 0 != string.Compare(t.BaseType, "System.Void", StringComparison.InvariantCulture)); })) { Exception e; // Written this way to pass the Code Contracts style requirements. e = new ArgumentNullException("indexes"); throw e; } if (match == null || match.Length == 0) { throw new ArgumentException("The array cannot be null or empty", nameof(match)); } var candidates = (CodeMemberProperty[])match.Clone(); int i, j = 0; // Find all the properties that can be described by type indexes parameter int CurIdx = 0; int indexesLength = (indices != null) ? indices.Length : 0; for (i = 0; i < candidates.Length; i++) { if (indices != null) { var par = candidates[i].Parameters; if (par.Count != indexesLength) { continue; } for (j = 0; j < indexesLength; j++) { var pCls = par[j].Type; if (null == pCls || 0 == string.Compare("System.Void", pCls.BaseType, StringComparison.InvariantCulture)) { continue; } // If the classes exactly match continue if (pCls == indices[j]) { continue; } if (0 == pCls.ArrayRank && 0 == string.Compare("System.Object", pCls.BaseType)) { continue; } if (CodeDomResolver.IsPrimitiveType(pCls)) { var type = indices[j]; if (!CodeDomResolver.IsPrimitiveType(type) || !_resolver.CanConvertTo(type, pCls, _scope)) { break; } } else { if (!_resolver.CanConvertTo(indices[j], pCls, _scope, false)) { break; } } } } if (j == indexesLength) { if (returnType != null) { if (CodeDomResolver.IsPrimitiveType(candidates[i].Type)) { if (CodeDomResolver.IsPrimitiveType(returnType) || !_resolver.CanConvertTo(returnType, candidates[i].Type, _scope)) { continue; } } else { if (!_resolver.CanConvertTo(returnType, candidates[i].Type, _scope, false)) { continue; } } } candidates[CurIdx++] = candidates[i]; } } if (CurIdx == 0) { return(null); } if (CurIdx == 1) { return(candidates[0]); } // Walk all of the properties looking the most specific method to invoke int currentMin = 0; bool ambig = false; int[] paramOrder = new int[indexesLength]; for (i = 0; i < indexesLength; i++) { paramOrder[i] = i; } for (i = 1; i < CurIdx; i++) { int newMin = FindMostSpecificType(candidates[currentMin].Type, candidates[i].Type, returnType); if (newMin == 0 && indices != null) { newMin = FindMostSpecific(_GetParamInfos(candidates[currentMin].Parameters, _scope), paramOrder, null, _GetParamInfos(candidates[i].Parameters, _scope), paramOrder, null, indices, null); } if (newMin == 0) { newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]); if (newMin == 0) { ambig = true; } } if (newMin == 2) { ambig = false; currentMin = i; } } if (ambig) { throw new AmbiguousMatchException("Multiple members matched the target argument types"); } return(candidates[currentMin]); }
static CodeDelegateCreateExpression _GetDelegateFromFields(CodeObjectCreateExpression oc, CodeExpression target, CodeDomResolver res) { var v = target as CodeVariableReferenceExpression; if (null != v) { var scope = res.GetScope(v); if (scope.MemberNames.Contains(v.VariableName)) { return(new CodeDelegateCreateExpression(oc.CreateType, new CodeThisReferenceExpression(), v.VariableName)); } } throw new NotImplementedException(); }
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)); } } }
static void _Patch(CodeVariableReferenceExpression vr, CodeDomVisitContext ctx, CodeDomResolver resolver) { if (null != vr) { if ("NodeFlags" == vr.VariableName) { System.Diagnostics.Debugger.Break(); } var scope = resolver.GetScope(vr); if (scope.VariableTypes.ContainsKey(vr.VariableName)) { vr.UserData.Remove("slang:unresolved"); return; } // we need to replace it. if (scope.ArgumentTypes.ContainsKey(vr.VariableName)) { var a = new CodeArgumentReferenceExpression(vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, a); return; //args.Cancel = true; } else if (scope.FieldNames.Contains(vr.VariableName)) { CodeTypeReference tref; // find out where it belongs. if (scope.ThisTargets.Contains(vr.VariableName)) { var f = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, f); //return; } else if (scope.TypeTargets.TryGetValue(vr.VariableName, out tref)) { var f = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(tref), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, f); //return; } return; } else if (scope.MethodNames.Contains(vr.VariableName)) { CodeTypeReference tref; // find out where it belongs. if (scope.ThisTargets.Contains(vr.VariableName)) { var m = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, m); return; //args.Cancel = true; } if (scope.TypeTargets.TryGetValue(vr.VariableName, out tref)) { var m = new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(tref), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, m); return; //args.Cancel = true; } } else if (scope.PropertyNames.Contains(vr.VariableName)) { CodeTypeReference tref; // find out where it belongs. if (scope.ThisTargets.Contains(vr.VariableName)) { var p = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, p); return; //args.Cancel = true; } else if (scope.TypeTargets.TryGetValue(vr.VariableName, out tref)) { var p = new CodePropertyReferenceExpression(new CodeTypeReferenceExpression(tref), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, p); return; //args.Cancel = true; } } else if (scope.EventNames.Contains(vr.VariableName)) { CodeTypeReference tref; // find out where it belongs. if (scope.ThisTargets.Contains(vr.VariableName)) { var e = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, e); return; //args.Cancel = true; } else if (scope.TypeTargets.TryGetValue(vr.VariableName, out tref)) { var e = new CodeEventReferenceExpression(new CodeTypeReferenceExpression(tref), vr.VariableName); CodeDomVisitor.ReplaceTarget(ctx, e); return; //args.Cancel = true; } } return; } return; }