public override void VisitInvoke(ASTInvoke n) { if (SystemMethodManager.IsSystemMethod(n.Method)) { n.Actuals.Visit(this); SystemMethod method = SystemMethodManager.Lookup(n.Method); method.Emit(_gen); //store the return type so we can invoke methods and stuff on it. _lastWalkedType = _typeManager.LookupCilType(method.FuncInfo.ReturnType); } else { //push who n.Object.Visit(this); Type who = _lastWalkedType; //push actuals n.Actuals.Visit(this); //find the method to execute on the given class MethodBuilderInfo info = _typeManager.GetMethodBuilderInfo(who.Name, n.Method); _gen.Emit(OpCodes.Callvirt, info.Builder); //store the return type of the method _lastWalkedType = info.Builder.ReturnType; } }
public void CheckReadonlyNotPassedAsModifiable(MethodDescriptor methodDesc, ActualBuilder builder, ASTInvoke n) { //check for readonly params being passed as modifiable parameters. int index = 0; foreach (var actual in builder.Actuals) { if (actual.IsFromFormal && actual.IsFormalReadonly) { if (String.IsNullOrEmpty(methodDesc.Formals[index].Modifier) || !methodDesc.Formals[index].Modifier.Equals(READONLY_MODIFIER, StringComparison.OrdinalIgnoreCase)) { ReportError(n.Location, "Cannot pass readonly identifier '{0}' as a modifiable parameter to method '{1}'", actual.Name, methodDesc.Name); } } index++; } }
/// <summary> /// VisitInvoke and VisitDereferenceField are very similiar, so this should probably be refactored out /// into a common method. /// </summary> /// <param name="n"></param> public override void VisitInvoke(ASTInvoke n) { //Make sure the lvalue is a type and the method name exists CFlatType lhs = CheckSubTree(n.Object); if (lhs.IsClass) { TypeClass lvalue = (TypeClass)lhs; var descriptor = (ClassDescriptor)_scopeMgr.Find(lvalue.ClassName, p => p is ClassDescriptor); //check if a method with the given name exists in the scope. //This needs to check not only the class's shallow scope, but all the parents as well. MethodDescriptor methodDesc = _scopeMgr.Find(n.Method, d => d is MethodDescriptor, descriptor.Scope) as MethodDescriptor; //if (methodDesc != null && (descriptor.Methods.Contains(methodDesc) || methodDesc.IsCFlatMethod)) if (methodDesc != null) { if (methodDesc.Modifiers.Contains(PRIVATE_MODIFIER, StringComparer.InvariantCultureIgnoreCase)) { if (!_scopeMgr.HasSymbol(n.Method, _currentClass.Scope)) { ReportError(n.Location, "Cannot access the private method {0} in class {1} from class {2}", methodDesc.Name, methodDesc.ContainingClass.Name, _currentClass.ClassName); } } //check if the arguments match TypeFunction method = (TypeFunction)methodDesc.Type; //visit any actuals that need processing CheckSubTree(n.Actuals); //collect the actuals var curMethDesc = _scopeMgr.Find(_currentMethod.Name, d => d is MethodDescriptor) as MethodDescriptor; ActualBuilder builder = new ActualBuilder(curMethDesc); n.Actuals.Visit(builder); if (method.AcceptCall(builder.Actuals)) //if the types check { CheckActualsHaveReadonly(methodDesc, builder, n); CheckReadonlyNotPassedAsModifiable(methodDesc, builder, n); n.Descriptor = methodDesc; n.CFlatType = method.ReturnType; _lastSeenType = method.ReturnType; //check if we're processing an exit instruction, and if so, this counts as a return for the current block. if (IsExitMethod(n)) { _currentMethod.RegisterReturnStatement(); } } else { ReportError(n.Location, "Invalid parameters for method '{0}.{1}'", TypeToFriendlyName(lvalue), n.Method); } } else { ReportError(n.Location, "Method '{0}' does not exist for type '{1}'", n.Method, TypeToFriendlyName(lvalue)); } } else { ReportError(n.Location, "Type '{0}' does not support methods.", TypeToFriendlyName(lhs)); } }
public void CheckActualsHaveReadonly(MethodDescriptor desc, ActualBuilder builder, ASTInvoke n) { //check that for all readonly formals the actual is being passes with a readonly modifier int index = 0; foreach (var formal in desc.Formals) { if (!String.IsNullOrEmpty(formal.Modifier) && formal.Modifier.Equals(READONLY_MODIFIER, StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(builder.Actuals[index].Modifier) || !builder.Actuals[index].Modifier.Equals(READONLY_MODIFIER, StringComparison.OrdinalIgnoreCase)) { ReportError(n.Location, "Missing readonly declaration on method invocation - Parameter '{0}' on function '{1}' must be marked as readonly.", formal.Name, desc.Name); } } index++; } }
/// <summary> /// Returns whether or not the invoked method "counts" as a return statement. /// This is equivalent to C# where if a method that has a return value throws an exception, its decided /// to return control properly. /// </summary> /// <param name="n"></param> /// <returns></returns> private bool IsExitMethod(ASTInvoke n) { return(_systemMethods.Any(m => m.Name == n.Method && m.IsExitStatement)); }