/* valadelegatetype.vala * * Copyright (C) 2007-2012 Jürg Billeter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jürg Billeter */ using GLib; /** * The type of an instance of a delegate. */ public class Vala.DelegateType : CallableType { public weak Delegate delegate_symbol { get { return (Delegate) symbol; } } public bool is_called_once { get; set; } DelegateTargetField? target_field; DelegateDestroyField? destroy_field; public DelegateType (Delegate delegate_symbol, SourceReference? source_reference = null) { base (delegate_symbol, source_reference); this.is_called_once = (delegate_symbol.get_attribute_string ("CCode", "scope") == "async"); } public override Symbol? get_member (string member_name) { if (member_name == "target") { return get_target_field (); } else if (member_name == "destroy") { return get_destroy_field (); } return null; } unowned DelegateTargetField get_target_field () { if (target_field == null) { target_field = new DelegateTargetField (source_reference); target_field.access = SymbolAccessibility.PUBLIC; } return target_field; } unowned DelegateDestroyField get_destroy_field () { if (destroy_field == null) { destroy_field = new DelegateDestroyField (source_reference); destroy_field.access = SymbolAccessibility.PUBLIC; } return destroy_field; } public override DataType copy () { var result = new DelegateType (delegate_symbol, source_reference); result.value_owned = value_owned; result.nullable = nullable; foreach (DataType arg in get_type_arguments ()) { result.add_type_argument (arg.copy ()); } result.is_called_once = is_called_once; return result; } public override bool equals (DataType type2) { return compatible (type2); } public override bool is_accessible (Symbol sym) { return delegate_symbol.is_accessible (sym); } public override bool check (CodeContext context) { if (is_called_once && !value_owned) { Report.warning (source_reference, "delegates with scope=\"async\" must be owned"); } if (!delegate_symbol.check (context)) { error = true; return false; } // check whether there is the expected amount of type-arguments if (!check_type_arguments (context, true)) { error = true; return false; } return true; } public override bool compatible (DataType target_type) { unowned DelegateType? dt_target = target_type as DelegateType; if (dt_target == null) { return false; } // trivial case if (delegate_symbol == dt_target.delegate_symbol) { return true; } if (delegate_symbol.has_target != dt_target.delegate_symbol.has_target) { return false; } // target-delegate is allowed to ensure stricter return type (stronger postcondition) if (!get_return_type ().stricter (dt_target.get_return_type ().get_actual_type (dt_target, null, this))) { return false; } var parameters = get_parameters (); Iterator params_it = parameters.iterator (); if (dt_target.delegate_symbol.parent_symbol is Signal && dt_target.delegate_symbol.sender_type != null && parameters.size == dt_target.get_parameters ().size + 1) { // target-delegate has sender parameter params_it.next (); // target-delegate is allowed to accept arguments of looser types (weaker precondition) var p = params_it.get (); if (!dt_target.delegate_symbol.sender_type.stricter (p.variable_type)) { return false; } } foreach (Parameter param in dt_target.get_parameters ()) { if (!params_it.next ()) { return false; } var p = params_it.get (); if (p != null && (param.ellipsis || param.params_array)) { if (param.ellipsis != p.ellipsis || param.params_array != p.params_array) { return false; } break; } // target-delegate is allowed to accept arguments of looser types (weaker precondition) if (!param.variable_type.get_actual_type (this, null, this).stricter (p.variable_type)) { return false; } } /* target-delegate may not expect more arguments */ if (params_it.next ()) { return false; } // target-delegate may throw less but not more errors than the delegate var error_types = new ArrayList (); delegate_symbol.get_error_types (error_types); foreach (DataType error_type in error_types) { bool match = false; var delegate_error_types = new ArrayList (); dt_target.delegate_symbol.get_error_types (delegate_error_types); foreach (DataType delegate_error_type in delegate_error_types) { if (error_type.compatible (delegate_error_type)) { match = true; break; } } if (!match) { return false; } } return true; } public override bool is_disposable () { return delegate_symbol.has_target && value_owned && !is_called_once; } }