/* valasymbol.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 a node in the symbol tree. */ public abstract class Vala.Symbol : CodeNode { /** * The parent of this symbol. */ public weak Symbol? parent_symbol { get { if (owner == null) { return null; } else { return owner.owner; } } } /** * The scope this symbol is a part of */ public weak Scope owner { get { return _owner; } set { _owner = value; _scope.parent_scope = value; } } /** * The symbol name. */ public string? name { get; set; } /** * Specifies whether this symbol is active. * * Symbols may become inactive when they only apply to a part of a * scope. This is used for local variables not declared at the beginning * of the block to determine which variables need to be freed before * jump statements. */ public bool active { get; set; default = true; } /** * Specifies whether this symbol has been accessed. */ public bool used { get; set; } /** * Specifies whether this symbol is anonymous and has no public definition. */ public bool anonymous { get; set; } /** * Specifies the accessibility of this symbol. Public accessibility * doesn't limit access. Default accessibility limits access to this * program or library. Private accessibility limits access to instances * of the contained type. */ public SymbolAccessibility access { get; set; } public Comment? comment { get; set; } private VersionAttribute _version; /** * The associated [Version] attribute */ public VersionAttribute version { get { if (_version == null) { _version = new VersionAttribute (this); } return _version; } } /** * Specifies whether this method explicitly hides a member of a base * type. */ public bool hides { get; set; } /** * Check if this symbol is just internal API (and therefore doesn't need * to be listed in header files for instance) by traversing parent symbols * and checking their accessibility. */ public bool is_internal_symbol () { if (!external && external_package) { // non-external symbols in VAPI files are internal symbols return true; } for (Symbol sym = this; null != sym; sym = sym.parent_symbol) { if (sym.access == SymbolAccessibility.PRIVATE || sym.access == SymbolAccessibility.INTERNAL) { return true; } } return false; } public bool is_private_symbol () { if (!external && external_package) { // non-external symbols in VAPI files are private symbols return true; } for (Symbol sym = this; null != sym; sym = sym.parent_symbol) { if (sym.access == SymbolAccessibility.PRIVATE) { return true; } } return false; } /** * The scope this symbol opens. */ public Scope scope { get { return _scope; } } public bool is_extern { get; set; } /** * Specifies whether the implementation is external, for example in * a separate C source file or in an external library. */ public bool external { get { if (_external != null) { return _external; } return is_extern || external_package; } set { _external = value; } } /** * Specifies whether the implementation is in an external library. */ public bool external_package { get { return source_type == SourceFileType.PACKAGE; } } /** * Specifies whether the implementation came from the commandline. */ public bool from_commandline { get { if (source_reference != null) { return source_reference.file.from_commandline; } else { return false; } } } /** * Gets the SourceFileType of the source file that this symbol * came from, or SourceFileType.NONE. */ public SourceFileType source_type { get { if (source_reference != null) { return source_reference.file.file_type; } else { return SourceFileType.NONE; } } } private weak Scope _owner; private Scope _scope; private bool? _external; protected Symbol (string? name, SourceReference? source_reference = null, Comment? comment = null) { this.name = name; this.source_reference = source_reference; this.comment = comment; _scope = new Scope (this); } /** * Returns the fully expanded name of this symbol for use in * human-readable messages. * * @return full name */ public string get_full_name () { if (parent_symbol == null) { return name; } if (name == null) { return parent_symbol.get_full_name (); } if (parent_symbol.get_full_name () == null) { return name; } if (name.has_prefix (".")) { return "%s%s".printf (parent_symbol.get_full_name (), name); } else { return "%s.%s".printf (parent_symbol.get_full_name (), name); } } /** * Converts a string from CamelCase to lower_case. * * @param camel_case a string in camel case * @return the specified string converted to lower case */ public static string camel_case_to_lower_case (string camel_case) { if ("_" in camel_case) { // do not insert additional underscores if input is not real camel case return camel_case.ascii_down (); } var result_builder = new StringBuilder (""); weak string i = camel_case; bool first = true; while (i.length > 0) { unichar c = i.get_char (); if (c.isupper () && !first) { /* current character is upper case and * we're not at the beginning */ weak string t = i.prev_char (); bool prev_upper = t.get_char ().isupper (); t = i.next_char (); bool next_upper = t.get_char ().isupper (); if (!prev_upper || (i.length >= 2 && !next_upper)) { /* previous character wasn't upper case or * next character isn't upper case*/ long len = result_builder.str.length; if (len != 1 && result_builder.str.get_char (len - 2) != '_') { /* we're not creating 1 character words */ result_builder.append_c ('_'); } } } result_builder.append_unichar (c.tolower ()); first = false; i = i.next_char (); } return result_builder.str; } /** * Converts a string from lower_case to CamelCase. * * @param lower_case a string in lower case * @return the specified string converted to camel case */ public static string lower_case_to_camel_case (string lower_case) { var result_builder = new StringBuilder (""); weak string i = lower_case; bool last_underscore = true; while (i.length > 0) { unichar c = i.get_char (); if (c == '_') { last_underscore = true; } else if (c.isupper ()) { // original string is not lower_case, don't apply transformation return lower_case; } else if (last_underscore) { result_builder.append_unichar (c.toupper ()); last_underscore = false; } else { result_builder.append_unichar (c); } i = i.next_char (); } return result_builder.str; } /** * Implementation of GLib.EqualFunc to use with e.g. HashMap * * @param a a symbol * @param b a symbol * @return whether the given instances represent the same symbol */ public static bool equal_func (Symbol a, Symbol b) { return str_equal (a.get_full_name (), b.get_full_name ()); } /** * Implementation of GLib.HashFunc to use with e.g. HashMap * * @param s a symbol * @return a hash value */ public static uint hash_func (Symbol s) { return str_hash (s.get_full_name ()); } // get the top scope from where this symbol is still accessible public Scope? get_top_accessible_scope (bool is_internal = false) { if (access == SymbolAccessibility.PRIVATE) { // private symbols are accessible within the scope where the symbol has been declared return owner; } if (access == SymbolAccessibility.INTERNAL) { is_internal = true; } if (parent_symbol == null) { // this is the root symbol if (is_internal) { // only accessible within the same library // return root scope return scope; } else { // unlimited access return null; } } // if this is a public symbol, it's equally accessible as the parent symbol return parent_symbol.get_top_accessible_scope (is_internal); } public virtual bool is_instance_member () { bool instance = true; if (this is Field) { var f = (Field) this; instance = (f.binding == MemberBinding.INSTANCE); } else if (this is Method) { var m = (Method) this; if (!(m is CreationMethod)) { instance = (m.binding == MemberBinding.INSTANCE); } } else if (this is Property) { var prop = (Property) this; instance = (prop.binding == MemberBinding.INSTANCE); } else if (this is EnumValue) { instance = false; } else if (this is ErrorCode) { instance = false; } return instance; } public virtual bool is_class_member () { bool isclass = true; if (this is Field) { var f = (Field) this; isclass = (f.binding == MemberBinding.CLASS); } else if (this is Method) { var m = (Method) this; if (!(m is CreationMethod)) { isclass = (m.binding == MemberBinding.CLASS); } } else if (this is Property) { var prop = (Property) this; isclass = (prop.binding == MemberBinding.CLASS); } else if (this is EnumValue) { isclass = false; } else if (this is ErrorCode) { isclass = false; } return isclass; } public Symbol? get_hidden_member () { Symbol sym = null; if (parent_symbol is Class) { var cl = ((Class) parent_symbol).base_class; while (cl != null) { sym = cl.scope.lookup (name); if (sym != null && sym.access != SymbolAccessibility.PRIVATE) { return sym; } cl = cl.base_class; } } else if (parent_symbol is Struct) { var st = ((Struct) parent_symbol).base_struct; while (st != null) { sym = st.scope.lookup (name); if (sym != null && sym.access != SymbolAccessibility.PRIVATE) { return sym; } st = st.base_struct; } } return null; } // check whether this symbol is at least as accessible as the specified symbol public bool is_accessible (Symbol sym) { Scope sym_scope = sym.get_top_accessible_scope (); Scope this_scope = this.get_top_accessible_scope (); if ((sym_scope == null && this_scope != null) || (sym_scope != null && !sym_scope.is_subscope_of (this_scope))) { return false; } return true; } public virtual void add_namespace (Namespace ns) { Report.error (ns.source_reference, "inner `%s' is not supported in `%s'", "namespace", get_full_name ()); } public virtual void add_class (Class cl) { Report.error (cl.source_reference, "inner `%s' types are not supported in `%s'", "class", get_full_name ()); } public virtual void add_interface (Interface iface) { Report.error (iface.source_reference, "inner `%s' types are not supported in `%s'", "interface", get_full_name ()); } public virtual void add_struct (Struct st) { Report.error (st.source_reference, "inner `%s' types are not supported in `%s'", "struct", get_full_name ()); } public virtual void add_enum (Enum en) { Report.error (en.source_reference, "inner `%s' types are not supported in `%s'", "enum", get_full_name ()); } public virtual void add_error_domain (ErrorDomain edomain) { Report.error (edomain.source_reference, "inner `%s' types are not supported in `%s'", "errordomain", get_full_name ()); } public virtual void add_delegate (Delegate d) { Report.error (d.source_reference, "inner `%s' types are not supported in `%s'", "delegate", get_full_name ()); } public virtual void add_constant (Constant constant) { Report.error (constant.source_reference, "constants are not allowed in `%s'", get_full_name ()); } public virtual void add_field (Field f) { Report.error (f.source_reference, "fields are not allowed in `%s'", get_full_name ()); } public virtual void add_method (Method m) { Report.error (m.source_reference, "methods are not allowed in `%s'", get_full_name ()); } public virtual void add_property (Property prop) { Report.error (prop.source_reference, "properties are not allowed in `%s'", get_full_name ()); } public virtual void add_signal (Signal sig) { Report.error (sig.source_reference, "signals are not allowed in `%s'", get_full_name ()); } public virtual void add_constructor (Constructor c) { Report.error (c.source_reference, "constructors are not allowed in `%s'", get_full_name ()); } public virtual void add_destructor (Destructor d) { Report.error (d.source_reference, "destructors are not allowed in `%s'", get_full_name ()); } public override string to_string () { var builder = new StringBuilder (get_full_name ()); unowned List? type_params = null; if (this is GenericSymbol) { type_params = ((GenericSymbol) this).get_type_parameters (); } if (type_params != null && type_params.size > 0) { builder.append_c ('<'); bool first = true; foreach (var type_param in type_params) { if (!first) { builder.append_c (','); } else { first = false; } builder.append (type_param.name); } builder.append_c ('>'); } return builder.str; } } public enum Vala.SymbolAccessibility { PRIVATE, INTERNAL, PROTECTED, PUBLIC; public unowned string to_string () { switch (this) { case PROTECTED: return "protected"; case INTERNAL: return "internal"; case PRIVATE: return "private"; case PUBLIC: return "public"; default: assert_not_reached (); } } } public enum Vala.MemberBinding { INSTANCE, CLASS, STATIC }