/// <summary> /// Someone is accessing a method on our ROOT object. We do the translation to C++ here. /// </summary> /// <param name="expr"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <returns></returns> /// <remarks>Static methods and instance methods are both handled correctly.</remarks> public IValue CodeMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container) { var objRef = ExpressionToCPP.InternalGetExpression(expr.Object, gc, null, container); StringBuilder bld = new StringBuilder(); if (expr.Method.IsStatic && objRef != null) { throw new ArgumentException(string.Format("Call to ROOT instance method '{0}' where the instance is null", expr.Method.Name)); } if (!expr.Method.IsStatic && objRef == null) { throw new ArgumentException(string.Format("Call to ROOT static method '{0}' where the instance is not null", expr.Method.Name)); } // // Code up the local invocation or the static invocation to the method // if (objRef != null) { bld.AppendFormat("{0}.{1}", objRef.AsObjectReference(), expr.Method.Name); } else { bld.AppendFormat("{0}::{1}", expr.Method.DeclaringType.Name.Substring(1), expr.Method.Name); } // // Put in the arguments // var argDep = AddMethodArguments(expr.Arguments, gc, container, bld); return(new ValSimple(bld.ToString(), expr.Type, objRef == null ? argDep : objRef.Dependants.Concat(argDep))); }
/// <summary> /// Called late to replace a constant expression of this type. By the time we get here these should not exist! /// The expression holder can't hold anything interesting (like parameters) - by the time we are here /// it is too late to do the parsing. /// </summary> /// <param name="expr"></param> /// <param name="codeEnv"></param> /// <param name="container"></param> /// <returns></returns> public IValue ProcessConstantReference(ConstantExpression expr, IGeneratedQueryCode codeEnv, CompositionContainer container) { var holder = expr.Value as IExpressionHolder; if (holder == null) { throw new InvalidOperationException("Can't get at the interface to get at the expression."); } var e = holder.HeldExpression; return(ExpressionToCPP.InternalGetExpression(e, codeEnv, null, container)); }
/// <summary> /// Convert something to a double. We don't actually do anything as long as this is an expression that we /// can naturally convert (int, float, etc.). /// /// We are expecting an expressio nthat is ToDouble(Convert()), so if we can't see the convert, then we bail. /// </summary> /// <param name="expr"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <param name="context"></param> /// <param name="container"></param> /// <returns></returns> private static IValue ProcessToDouble(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container) { var srcExpr = expr.Arguments[0]; if (srcExpr.NodeType != ExpressionType.Convert) { throw new NotImplementedException("Expecting a Convert expression inside the call to Convert.ToDouble"); } var cvtExpr = srcExpr as UnaryExpression; var result = ExpressionToCPP.InternalGetExpression(cvtExpr.Operand, gc, null, container); if (!result.Type.IsNumberType()) { throw new NotImplementedException("Do not know how to convert '" + srcExpr.Type.Name + "' to a double!"); } return(result); }
/// <summary> /// Translate the method call /// </summary> /// <param name="expr"></param> /// <param name="result"></param> /// <param name="gc"></param> /// <param name="context"></param> /// <returns></returns> public IValue CodeMethodCall(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container) { Init(); /// /// First see if we can't locate the method call that at least matches in names /// var matchingMethodNames = from kt in _knownTypes where kt.Name == expr.Method.DeclaringType.Name from m in kt.Methods where m.Name == expr.Method.Name select new { theType = kt, theMethod = m }; /// /// Next, match with the arguments /// var matchingMethod = from m in matchingMethodNames where m.theMethod.Arguments.Length == expr.Arguments.Count where m.theMethod.Arguments.Zip(expr.Arguments, (us, them) => new Tuple <KnownTypeInfo.MechodArg, Expression>(us, them)).All(apair => apair.Item1.Type == apair.Item2.Type.FullName) select m; /// /// Ok, at this point, we should have only one guy. If we have more then just choose the first /// var method = matchingMethod.FirstOrDefault(); if (method == null) { throw new ArgumentException("Could not find a matching method to translate for the call " + expr.ToString()); } /// /// And now translate the call /// StringBuilder rawValue = new StringBuilder(); rawValue.Append(method.theMethod.CPPName); rawValue.Append("("); bool first = true; var dependents = Enumerable.Empty <IDeclaredParameter>(); foreach (var arg in expr.Arguments.Zip(method.theMethod.Arguments, (m, a) => Tuple.Create(m, a))) { if (!first) { rawValue.Append(","); } first = false; var e = ExpressionToCPP.InternalGetExpression(arg.Item1, gc, null, container); rawValue.AppendFormat("({0}){1}", arg.Item2.CPPType, e.RawValue); dependents = dependents.Concat(e.Dependants); } rawValue.Append(")"); var result = new ValSimple(rawValue.ToString(), expr.Type, dependents); /// /// Include files /// foreach (var ifile in method.theMethod.IncludeFiles) { gc.AddIncludeFile(ifile); } /// /// We aren't re-writing this expression, so just return it. /// return(result); }
/// <summary> /// Build a code statement from the include files, the expression for the method call, and the generated lines of code. /// </summary> /// <param name="expr"></param> /// <param name="gc"></param> /// <param name="container"></param> /// <param name="includeFiles"></param> /// <param name="loc"></param> /// <returns></returns> public static IValue BuildCPPCodeStatement(MethodCallExpression expr, IGeneratedQueryCode gc, CompositionContainer container, string[] includeFiles, string[] loc) { // Get include files in. if (includeFiles != null) { foreach (var inc in includeFiles) { gc.AddIncludeFile(inc); } } // Next, go after the lines of code. We have to first sort out what parameter names we are looking at, // and then force a translation of those parameters into simple values we can pass to the C++ code we // are going to pull back. var paramsTranslated = from p in expr.Arguments.Zip(expr.Method.GetParameters(), (arg, param) => Tuple.Create(arg, param)) select new { Name = p.Item2.Name, Translated = ExpressionToCPP.InternalGetExpression(p.Item1, gc, null, container) }; var paramLookup = paramsTranslated.ToDictionary(v => v.Name, v => v.Translated.ApplyParensIfNeeded()); // Parse out the list of variables that are used. We will be passing these up the line as needed // so that we can tell how to optimize things. var dependents = new HashSet <string>(FindDeclarableParameters.FindAll(expr).Select(e => e.RawValue)); // We also need a return variable. Since this can be multiple lines of code and we don't // know how the result will be used, we have to declare it up front... and pray they // use it correctly! :-) var cppResult = DeclarableParameter.CreateDeclarableParameterExpression(expr.Type); var cppStatement = new CPPCodeStatement(expr.Method, cppResult, loc, dependents); gc.Add(cppStatement); gc.Add(cppResult); paramLookup.Add(expr.Method.Name, cppResult.RawValue); var result = new ValSimple(cppResult.RawValue, expr.Type, DeclarableParameter.CreateDeclarableParameterExpression(cppResult.RawValue, expr.Type).AsArray()); // Make sure a result exists in here! This at least will prevent some bad C++ code from getting generated! var lookForResult = new Regex(string.Format(@"\b{0}\b", expr.Method.Name)); bool didReference = loc.Any(l => lookForResult.Match(l).Success); if (!didReference) { throw new ArgumentException(string.Format("The C++ code attached to the method '{0}' doesn't seem to set a result.", expr.Method.Name)); } // Figure out if there are any Unique variables. If there are, then we need to do // a replacement on them. var findUnique = new Regex(@"\b\w*Unique\b"); var varUniqueRequests = (from l in loc let matches = findUnique.Matches(l) from m in Enumerable.Range(0, matches.Count) select matches[m].Value).Distinct(); foreach (var varRepl in varUniqueRequests) { var uniqueName = varRepl.Substring(0, varRepl.Length - "Unique".Length); var uniqueTranslated = uniqueName + _uniqueCounter.ToString(); cppStatement.AddUniqueVariable(varRepl, uniqueTranslated); _uniqueCounter++; } // Add the parameters that need to be translated here. foreach (var paramName in paramLookup) { cppStatement.AddParamReplacement(paramName.Key, paramName.Value); } return(result); }