/* valainterface.vala * * Copyright (C) 2006-2010 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; /** * Represents an interface declaration in the source code. */ public class Vala.Interface : ObjectTypeSymbol { private List prerequisites = new ArrayList (); private List virtuals = new ArrayList (); /** * Creates a new interface. * * @param name type name * @param source_reference reference to source code * @return newly created interface */ public Interface (string name, SourceReference? source_reference = null, Comment? comment = null) { base (name, source_reference, comment); } /** * Adds the specified interface or class to the list of prerequisites of * this interface. * * @param type an interface or class reference */ public void add_prerequisite (DataType type) { prerequisites.add (type); type.parent_node = this; } /** * Returns the base type list. * * @return list of base types */ public unowned List get_prerequisites () { return prerequisites; } /** * Adds the specified method as a member to this interface. * * @param m a method */ public override void add_method (Method m) { if (m is CreationMethod) { Report.error (m.source_reference, "construction methods may only be declared within classes and structs"); m.error = true; return; } if (m.binding != MemberBinding.STATIC) { m.this_parameter = new Parameter ("this", SemanticAnalyzer.get_this_type (m, this), m.source_reference); m.scope.add (m.this_parameter.name, m.this_parameter); } if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) { m.result_var = new LocalVariable (m.return_type.copy (), "result", null, m.source_reference); m.result_var.is_result = true; } base.add_method (m); } /** * Adds the specified property as a member to this interface. * * @param prop a property */ public override void add_property (Property prop) { if (prop.field != null) { Report.error (prop.source_reference, "interface properties should be `abstract' or have `get' accessor and/or `set' mutator"); prop.error = true; return; } base.add_property (prop); if (prop.binding != MemberBinding.STATIC) { prop.this_parameter = new Parameter ("this", SemanticAnalyzer.get_this_type (prop, this), prop.source_reference); prop.scope.add (prop.this_parameter.name, prop.this_parameter); } } public virtual List get_virtuals () { return virtuals; } public override void accept (CodeVisitor visitor) { visitor.visit_interface (this); } public override void accept_children (CodeVisitor visitor) { foreach (DataType type in prerequisites) { type.accept (visitor); } // Invoke common implementation in ObjectTypeSymbol base.accept_children (visitor); } public override bool is_reference_type () { return true; } public override bool is_subtype_of (TypeSymbol t) { if (this == t) { return true; } foreach (DataType prerequisite in prerequisites) { if (prerequisite.type_symbol != null && prerequisite.type_symbol.is_subtype_of (t)) { return true; } } return false; } public override void replace_type (DataType old_type, DataType new_type) { for (int i = 0; i < prerequisites.size; i++) { if (prerequisites[i] == old_type) { prerequisites[i] = new_type; new_type.parent_node = this; return; } } } public override bool check (CodeContext context) { if (checked) { return !error; } if (!base.check (context)) { return false; } checked = true; var old_source_file = context.analyzer.current_source_file; var old_symbol = context.analyzer.current_symbol; if (source_reference != null) { context.analyzer.current_source_file = source_reference.file; } context.analyzer.current_symbol = this; foreach (DataType prerequisite_reference in get_prerequisites ()) { // check whether prerequisite is at least as accessible as the interface if (!prerequisite_reference.is_accessible (this)) { error = true; Report.error (source_reference, "prerequisite `%s' is less accessible than interface `%s'", prerequisite_reference.to_string (), get_full_name ()); return false; } // check whether there is the expected amount of type-arguments if (!prerequisite_reference.check_type_arguments (context)) { error = true; return false; } } /* check prerequisites */ Class prereq_class = null; foreach (DataType prereq in get_prerequisites ()) { if (!(prereq is ObjectType)) { error = true; Report.error (source_reference, "Prerequisite `%s' of interface `%s' is not a class or interface", prereq.to_string (), get_full_name ()); return false; } /* interfaces are not allowed to have multiple instantiable prerequisites */ if (prereq.type_symbol is Class) { if (prereq_class != null) { error = true; Report.error (source_reference, "%s: Interfaces cannot have multiple instantiable prerequisites (`%s' and `%s')", get_full_name (), prereq.type_symbol.get_full_name (), prereq_class.get_full_name ()); return false; } prereq_class = (Class) prereq.type_symbol; } } foreach (DataType type in prerequisites) { type.check (context); context.analyzer.check_type (type); } foreach (TypeParameter p in get_type_parameters ()) { p.check (context); } foreach (Enum en in get_enums ()) { en.check (context); } foreach (Field f in get_fields ()) { f.check (context); } foreach (Constant c in get_constants ()) { c.check (context); } if (context.abi_stability) { foreach (Symbol s in get_members ()) { if (s is Method) { var m = (Method) s; m.check (context); if (m.is_virtual || m.is_abstract) { virtuals.add (m); } } else if (s is Signal) { var sig = (Signal) s; sig.check (context); if (sig.is_virtual) { virtuals.add (sig); } } else if (s is Property) { var prop = (Property) s; prop.check (context); if (prop.is_virtual || prop.is_abstract) { virtuals.add (prop); } } } } else { foreach (Method m in get_methods ()) { m.check (context); if (m.is_virtual || m.is_abstract) { virtuals.add (m); } } foreach (Signal sig in get_signals ()) { sig.check (context); if (sig.is_virtual) { virtuals.add (sig); } } foreach (Property prop in get_properties ()) { prop.check (context); if (prop.is_virtual || prop.is_abstract) { virtuals.add (prop); } } } foreach (Class cl in get_classes ()) { cl.check (context); } foreach (Interface iface in get_interfaces ()) { iface.check (context); } foreach (Struct st in get_structs ()) { st.check (context); } foreach (Delegate d in get_delegates ()) { d.check (context); } Map? positions = new HashMap (); bool ordered_seen = false; bool unordered_seen = false; foreach (Symbol sym in virtuals) { int ordering = sym.get_attribute_integer ("CCode", "ordering", -1); if (ordering < -1) { Report.error (sym.source_reference, "%s: Invalid ordering", sym.get_full_name ()); // Mark state as invalid error = true; ordered_seen = true; unordered_seen = true; continue; } bool ordered = ordering != -1; if (ordered && unordered_seen && !ordered_seen) { Report.error (sym.source_reference, "%s: Cannot mix ordered and unordered virtuals", sym.get_full_name ()); error = true; } ordered_seen = ordered_seen || ordered; if (!ordered && !unordered_seen && ordered_seen) { Report.error (sym.source_reference, "%s: Cannot mix ordered and unordered virtuals", sym.get_full_name ()); error = true; } unordered_seen = unordered_seen || !ordered; if (!ordered_seen || !unordered_seen) { if (ordered) { Symbol? prev = positions[ordering]; if (prev != null) { Report.error (sym.source_reference, "%s: Duplicate ordering (previous virtual with the same position is %s)", sym.get_full_name (), prev.name); error = true; } positions[ordering] = sym; } } } if (ordered_seen) { for (int i = 0; i < virtuals.size; i++) { Symbol? sym = positions[i]; if (sym == null) { Report.error (source_reference, "%s: Gap in ordering in position %d", get_full_name (), i); error = true; } if (!error) { virtuals[i] = sym; } } } context.analyzer.current_source_file = old_source_file; context.analyzer.current_symbol = old_symbol; return !error; } }