// Nothing to resolve for literal expressions protected override Exp ResolveExpAsRight(ISemanticResolver s) { // Always resolve our children ResolveExpAsRight(ref m_left, s); ResolveExpAsRight(ref m_right, s); // If we don't match a predefined operator, then check for overloads if (!MatchesPredefinedOp(Op, this.Left.CLRType, this.Right.CLRType)) { // Packagage Left & Right into parameters for a method call ArgExp [] args = new ArgExp [2] { new ArgExp(EArgFlow.cIn, m_left), new ArgExp(EArgFlow.cIn, m_right) }; // Check for delegate combination // D operator+(D, D) // D operator-(D, D) if (AST.DelegateDecl.IsDelegate(m_left.CLRType)) { if (m_left.CLRType == m_right.CLRType) { if (Op == BinaryOp.cAdd || Op == BinaryOp.cSub) { System.Type d = m_left.CLRType; // Translates to: // op+ --> (D) System.Delegate.Combine(left, right) // op- --> (D) System.Delegate.Remove(left, right) TypeEntry tDelegate = s.LookupSystemType("MulticastDelegate"); string stName = (Op == BinaryOp.cAdd) ? "Combine" : "Remove"; bool dummy; MethodExpEntry sym = tDelegate.LookupMethod(s, new Identifier(stName), new Type[] { d, d}, out dummy); Exp call2 = new CastObjExp( new ResolvedTypeSig(d, s), new MethodCallExp( null,sym, args, s) ); Exp.ResolveExpAsRight(ref call2, s); return call2; } } } // end delgate op+ check // Check for System.String.Concat(). // @todo - this should be able to compress an entire subtree, not just 2 args. // (ie, a+b+c -> String.Concat(a,b,c); // So we can't merge this into the SearchForOverload. // But for now we'll be lazy... if ((Op == BinaryOp.cAdd) && (Left.CLRType == typeof(string) || Right.CLRType == typeof(string))) { Exp call2 = new MethodCallExp( new DotObjExp( new SimpleObjExp( new Identifier("System", this.m_filerange) ), new Identifier("String", this.m_filerange)), new Identifier("Concat", m_filerange), args); call2.SetLocation(this.Location); Exp.ResolveExpAsRight(ref call2, s); return call2; } MethodExpEntry m = SearchForOverloadedOp(s); if (m == null && (Op == BinaryOp.cEqu || Op == BinaryOp.cNeq)) { // If it's '==' or '!=', then it's ok if we didn't find // an overload. } else { // Couldn't find an overload, throw error if (m == null) { //ThrowError_NoAcceptableOperator(s, this.Location, m_left.CLRType, m_right.CLRType, Op); ThrowError(SymbolError.NoAcceptableOperator(this.Location, m_left.CLRType, m_right.CLRType, Op)); } // Replace this node w/ the method call MethodCallExp call = new MethodCallExp(null, m, args, s); call.SetLocation(this.Location); return call; } } CalcCLRType(s); return this; }
//----------------------------------------------------------------------------- // Parse a parameter list (including opening & closing parens) // paramlist-> param ',' param ',' ... // param-> (''|'ref'|'out') exp //----------------------------------------------------------------------------- protected ArgExp[] ParseArgList() { ArrayList al = new ArrayList(); ReadExpectedToken(Token.Type.cLParen); // Keep parsing expressions until we hit the closing ')' Token t = m_lexer.PeekNextToken(); if (t.TokenType == Token.Type.cRParen) ConsumeNextToken(); while(t.TokenType != Token.Type.cRParen) { t = m_lexer.PeekNextToken(); // Parse an expression and add it to the list EArgFlow eFlow = EArgFlow.cIn; if (t.TokenType == Token.Type.cOut) eFlow = EArgFlow.cOut; if (t.TokenType == Token.Type.cRef) eFlow = EArgFlow.cRef; if (eFlow != EArgFlow.cIn) { ConsumeNextToken(); } Exp e = ParseExp(); e = new ArgExp(eFlow, e); al.Add(e); // Skip past the comma (or read the closing ')' ) t = m_lexer.GetNextToken(); CheckError_UnexpectedToken(t, new Token.Type [] { Token.Type.cComma, Token.Type.cRParen } ); } // Convert to real array ArgExp[] a = new ArgExp[al.Count]; for(int i = 0; i < al.Count; i++) a[i] = (ArgExp) al[i]; return a; }
// Use this when we already have a static method to call // and we already have the symbols public MethodCallExp( Exp eInstance, // null if static MethodExpEntry symMethod, ArgExp [] arParams, ISemanticResolver s ) { this.m_idName = new Identifier(symMethod.Name); m_arParams = arParams; m_symbol = symMethod; // Spoof Left if (eInstance == null) { //m_objExp = new SimpleObjExp(symMethod.SymbolClass.Name); m_objExp = new TypeExp(symMethod.SymbolClass); } else m_objExp = eInstance; Exp.ResolveExpAsRight(ref m_objExp, s); // Resolve args, just in case foreach(ArgExp eArg in arParams) { Exp e = eArg; Exp.ResolveExpAsRight(ref e, s); Debug.Assert(e == eArg); } //ResolveAsExpEntry(m_symbol, s); CalcCLRType(s); }
// Semantic resolution protected override Exp ResolveExpAsRight(ISemanticResolver s) { // Only resolve once. if (m_symbol != null) return this; // First, resolve our parameters (because of overloading) // We need to know the URT types for our parameters // in order to resolve between overloaded operators Type [] alParamTypes = new Type[m_arParams.Length]; for(int i = 0; i < m_arParams.Length; i++) { Exp e = m_arParams[i]; ResolveExpAsRight(ref e, s); Debug.Assert(e == m_arParams[i]); Type tParam = e.CLRType; //if ((tParam !=null) && tParam.IsByRef) // tParam = tParam.GetElementType(); alParamTypes[i] = tParam; //Debug.Assert(alParamTypes[i] != null); } TypeEntry tCur = s.GetCurrentClass(); TypeEntry tLeft = null; // Type to lookup in // Is this a 'base' access? // Convert to the real type and set a non-virtual flag if (m_objExp is SimpleObjExp) { SimpleObjExp e = m_objExp as SimpleObjExp; if (e.Name.Text == "base") { // Set the scope that we lookup in. tLeft = tCur.Super; // Still need to resolve the expression. m_objExp = new SimpleObjExp("this"); m_fIsNotPolymorphic = true; } } #if true // See if we have a delegate here Exp eDelegate = null; if (m_objExp == null) { Exp e = new SimpleObjExp(m_idName); Exp.ResolveExpAsRight(ref e, s); if (!(e is SimpleObjExp)) eDelegate = e; } else { // If it's an interface, then we know we can't have a delegate field on it, // so short-circuit now. Exp.ResolveExpAsRight(ref m_objExp, s); if (!m_objExp.CLRType.IsInterface) { Exp e = new DotObjExp(m_objExp, m_idName); Exp.ResolveExpAsRight(ref e, s); if (!(e is DotObjExp)) eDelegate = e; } } if (eDelegate != null) { if (!DelegateDecl.IsDelegate(eDelegate.CLRType)) { //Debug.Assert(false, "@todo - " + m_strName + " is not a delegate or function"); // @todo - legit // Just fall through for now, method resolution will decide if this is a valid function } else { Exp e = new MethodCallExp( eDelegate, new Identifier("Invoke"), this.m_arParams ); Exp.ResolveExpAsRight(ref e, s); return e; } } #endif // No delegate, carry on with a normal function call // If there's no objexp, then the function is a method // of the current class. // make it either a 'this' or a static call if (m_objExp == null) { // Lookup bool fIsVarArgDummy; MethodExpEntry sym = tCur.LookupMethod(s, m_idName, alParamTypes, out fIsVarArgDummy); if (sym.IsStatic) { m_objExp = new TypeExp(tCur); } else { m_objExp = new SimpleObjExp("this"); } } // Need to Lookup m_strName in m_objExp's scope (inherited scope) Exp.ResolveExpAsRight(ref m_objExp, s); // Get type of of left side object // This call can either be a field on a variable // or a static method on a class bool fIsStaticMember = false; // If we don't yet know what TypeEntry this methodcall is on, then figure // it out based off the expression if (tLeft == null) { if (m_objExp is TypeExp) { fIsStaticMember = true; tLeft = ((TypeExp) m_objExp).Symbol; } else { fIsStaticMember = false; tLeft = s.ResolveCLRTypeToBlueType(m_objExp.CLRType); } } // Here's the big lookup. This will jump through all sorts of hoops to match // parameters, search base classes, do implied conversions, varargs, // deal with abstract, etc. bool fIsVarArg; m_symbol = tLeft.LookupMethod(s, m_idName, alParamTypes, out fIsVarArg); Debug.Assert(m_symbol != null); if (m_fIsNotPolymorphic) { // of the form 'base.X(....)' if (m_symbol.IsStatic) ThrowError(SymbolError.BaseAccessCantBeStatic(this.Location, m_symbol)); // @todo - PrintError? } else { // normal method call /* if (fIsStaticMember && !m_symbol.IsStatic) ThrowError(SymbolError.ExpectInstanceMember(this.Location)); // @todo - PrintError? else if (!fIsStaticMember && m_symbol.IsStatic) ThrowError(SymbolError.ExpectStaticMember(this.Location)); // @todo - PrintError? */ Debug.Assert(fIsStaticMember == m_symbol.IsStatic, "@todo - user error. Mismatch between static & instance members on line."); } // If we have a vararg, then transform it if (fIsVarArg) { // Create the array int cDecl = m_symbol.ParamCount; int cCall = this.ParamExps.Length; ArrayTypeSig tSig = new ArrayTypeSig(m_symbol.ParamCLRType(cDecl - 1), s); Node [] list = new Node[cCall - cDecl + 1]; for(int i = 0; i < list.Length; i++) { list[i] = this.ParamExps[i + cDecl - 1]; } Exp eArray = new NewArrayObjExp( tSig, new ArrayInitializer( list ) ); Exp.ResolveExpAsRight(ref eArray, s); // Change the parameters to use the array ArgExp [] arParams = new ArgExp[cDecl]; for(int i = 0; i < cDecl - 1; i++) arParams[i] = m_arParams[i]; arParams[cDecl - 1] = new ArgExp(EArgFlow.cIn, eArray); m_arParams = arParams; } // end vararg transformation this.CalcCLRType(s); return this; }
public MethodCallExp( Exp e, // may be null, Identifier id, ArgExp [] arParams ) { // m_objExp may be null _until_ we resolve this. And then it's either // going to the implied 'this' ptr, a global func or a static func. m_objExp = e; m_idName = id; m_arParams = (arParams == null) ? new ArgExp[0] : arParams; // @todo - set in parser m_filerange = id.Location; }