
// // Copyright (c) 2006, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 15 Sep 05 Brian Frank Creation // 5 Sep 06 Brian Frank Ported from Java to Fan // ** ** CallResolver handles the process of resolving a CallExpr or ** UnknownVarExpr to a method call or a field access. ** class CallResolver : CompilerSupport { ////////////////////////////////////////////////////////////////////////// // Construction ////////////////////////////////////////////////////////////////////////// ** ** Construct with NameExpr (base class of CallExpr and UnknownVarExpr) ** new make(Compiler compiler, TypeDef curType, MethodDef curMethod, NameExpr expr) : super(compiler) { this.curType = curType this.curMethod = curMethod this.expr = expr this.location = expr.location this.target = expr.target this.name = expr.name call := expr as CallExpr if (call != null) { this.isVar = false this.args = call.args this.found = call.method } else { this.isVar = true this.args = Expr[,] } } ////////////////////////////////////////////////////////////////////////// // Resolve ////////////////////////////////////////////////////////////////////////// ** ** Resolve into a method call or field access ** Expr resolve() { try { resolveBase find insertImplicitThis resolveToExpr constantFolding return result } catch (CompilerErr err) { expr.ctype = ns.error return expr } } ////////////////////////////////////////////////////////////////////////// // Resolve Base ////////////////////////////////////////////////////////////////////////// ** ** Resolve the base type which defines the slot we are calling. ** Void resolveBase() { // if target unspecified, then assume a slot on the current // class otherwise the slot must be on the target type if (target == null) { // if we are in a closure - then base is the enclosing class if (curType.isClosure) { base = curType.closure.enclosingType } else { base = curType } } else { base = target.ctype } // if base is the error type, then we already logged an error // trying to resolve the target and it's pointless to continue if (base == ns.error) throw CompilerErr.make("ignore", location, null) // sanity check if (base == null) throw err("Internal error", location) } ////////////////////////////////////////////////////////////////////////// // Find ////////////////////////////////////////////////////////////////////////// ** ** Find the method or field with the specified name. ** Void find() { // if already "found", then skip this step if (found != null) return // look it up in base type found = base.slot(name) // if still not found, then error if (found == null) { if (isVar) { if (target == null) throw err("Unknown variable '$name'", location) else throw err("Unknown slot '$errSig'", location) } else { throw err("Unknown method '$errSig'", location) } } // if slot is a field and we are using method call // syntax on a field and need to map to getter/setter field := found as CField if (field != null && !isVar) throw err("Expected method, not field '$errSig'", location) } private Str errSig() { return "${base.qname}.${name}" } ////////////////////////////////////////////////////////////////////////// // Implicit This ////////////////////////////////////////////////////////////////////////// ** ** If the call has no explicit target, and is a instance field ** or method, then we need to insert an implicit this. ** private Void insertImplicitThis() { if (target != null) return if (found.isStatic || found.isCtor) return if (curMethod.isStatic) return if (curType.isClosure) { closure := curType.closure if (!closure.enclosingMethod.isStatic) target = FieldExpr.make(location, ThisExpr.make(location, closure.enclosingType), closure.outerThisField) } else { target = ThisExpr.make(location, curType) } } ////////////////////////////////////////////////////////////////////////// // Resolve Expr Type ////////////////////////////////////////////////////////////////////////// ** ** Compute the expression type the call itself (what gets left on the stack). ** private Void resolveToExpr() { if (found is CField) { result = resolveToFieldExpr } else { result = resolveToCallExpr } } private CallExpr resolveToCallExpr() { method := (CMethod)found call := expr as CallExpr if (call == null) { call = CallExpr.make(location) call.name = name call.args = args } call.target = target call.method = method if (method.isCtor) call.ctype = method.parent else call.ctype = method.returnType return call } private FieldExpr resolveToFieldExpr() { f := (CField)found field := FieldExpr.make(location) field.target = target field.name = name field.field = f field.ctype = f.fieldType return field } ////////////////////////////////////////////////////////////////////////// // Constant Folding ////////////////////////////////////////////////////////////////////////// ** ** If the epxression is a call, check for constant folding. ** private Void constantFolding() { call := result as CallExpr if (call != null) result = ConstantFolder.make(compiler).fold(call) } ////////////////////////////////////////////////////////////////////////// // Fields ////////////////////////////////////////////////////////////////////////// TypeDef curType // current type of scope MethodDef curMethod // current method of scope NameExpr expr // original expression being resolved Location location // location of original expression Expr target // target base or null Str name // slot name to resolve Bool isVar // are we resolving simple variable Expr[] args // arguments or null if simple variable CType base // resolveBase() CSlot found // find() Expr result // resolveToExpr() }