/* valagidlparser.vala * * Copyright (C) 2006-2010 Jürg Billeter * Copyright (C) 2006-2008 Raffaele Sandrini * * 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 * Raffaele Sandrini */ using GLib; /** * Code visitor parsing all GIDL files. */ public class Vala.GIdlParser : CodeVisitor { private CodeContext context; private SourceFile current_source_file; private SourceReference current_source_reference; private Namespace current_namespace; private TypeSymbol current_data_type; private Map codenode_attributes_map; private Map codenode_attributes_patterns; private Set current_type_symbol_set; private Map cname_type_map; static GLib.Regex type_from_string_regex; /** * Parse all source files in the specified code context and build a * code tree. * * @param context a code context */ public void parse (CodeContext context) { cname_type_map = new HashMap (str_hash, str_equal); this.context = context; context.accept (this); cname_type_map = null; } public override void visit_namespace (Namespace ns) { ns.accept_children (this); } public override void visit_class (Class cl) { visit_type (cl); } public override void visit_struct (Struct st) { visit_type (st); } public override void visit_interface (Interface iface) { visit_type (iface); } public override void visit_enum (Enum en) { visit_type (en); } public override void visit_error_domain (ErrorDomain ed) { visit_type (ed); } public override void visit_delegate (Delegate d) { visit_type (d); } private void visit_type (TypeSymbol t) { if (!cname_type_map.contains (get_cname (t))) { cname_type_map[get_cname (t)] = t; } } public override void visit_source_file (SourceFile source_file) { if (source_file.filename.has_suffix (".gi")) { parse_file (source_file); } } private void parse_file (SourceFile source_file) { string metadata_filename = "%s.metadata".printf (source_file.filename.substring (0, source_file.filename.length - ".gi".length)); current_source_file = source_file; codenode_attributes_map = new HashMap (str_hash, str_equal); codenode_attributes_patterns = new HashMap (direct_hash, (EqualFunc) PatternSpec.equal); if (FileUtils.test (metadata_filename, FileTest.EXISTS)) { try { string metadata; FileUtils.get_contents (metadata_filename, out metadata, null); foreach (unowned string line in metadata.split ("\n")) { if (line.has_prefix ("#")) { // ignore comment lines continue; } var tokens = line.split (" ", 2); if (null == tokens[0]) { continue; } if (-1 != tokens[0].index_of_char ('*')) { PatternSpec* pattern = new PatternSpec (tokens[0]); codenode_attributes_patterns[pattern] = tokens[0]; } codenode_attributes_map[tokens[0]] = tokens[1]; } } catch (FileError e) { Report.error (null, "Unable to read metadata file: %s", e.message); } } try { var modules = Idl.parse_file (source_file.filename); current_source_reference = new SourceReference (source_file, SourceLocation (null, 0, 0), SourceLocation (null, 0, 0)); foreach (weak IdlModule module in modules) { var ns = parse_module (module); if (ns != null) { context.root.add_namespace (ns); } } } catch (MarkupError e) { Report.error (null, "Unable to parse GIDL file: %s", e.message); } } private string fix_type_name (string type_name, Symbol container) { var attributes = get_attributes (type_name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "name") { return eval (nv[1]); } } } if (type_name.has_prefix (container.name)) { return type_name.substring (container.name.length); } else { var cprefix = get_cprefix (container); if (type_name.has_prefix (cprefix)) { return type_name.substring (cprefix.length);; } } return type_name; } private string fix_const_name (string const_name, Symbol container) { var pref = get_lower_case_cprefix (container).ascii_up (); if (const_name.has_prefix (pref)) { return const_name.substring (pref.length); } return const_name; } private string get_cheader_filename (Symbol sym) { var cheader_filename = sym.get_attribute_string ("CCode", "cheader_filename"); if (cheader_filename != null) { return cheader_filename; } if (sym.parent_symbol != null) { return get_cheader_filename (sym.parent_symbol); } else if (sym.source_reference != null) { return sym.source_reference.file.get_cinclude_filename (); } return ""; } private string get_cname (Symbol sym, Symbol? container = null) { if (container == null) { container = sym.parent_symbol; } var cname = sym.get_attribute_string ("CCode", "cname"); if (cname != null) { return cname; } if (sym is Method) { var name = sym.name; if (sym is CreationMethod) { if (name == null || name == ".new") { name = "new"; } else { name = "new_%s".printf (name); } } if (container != null) { return "%s%s".printf (get_lower_case_cprefix (container), name); } else { return name; } } else { if (container != null) { return "%s%s".printf (get_cprefix (container), sym.name); } else { return sym.name; } } } private string get_finish_cname (Method m) { var finish_cname = m.get_attribute_string ("CCode", "finish_name"); if (finish_cname != null) { return finish_cname; } var result = get_cname (m); if (result.has_suffix ("_async")) { result = result.substring (0, result.length - "_async".length); } return result + "_finish"; } private string get_lower_case_cname (Symbol sym) { var lower_case_csuffix = Symbol.camel_case_to_lower_case (sym.name); if (sym is ObjectTypeSymbol) { // remove underscores in some cases to avoid conflicts of type macros if (lower_case_csuffix.has_prefix ("type_")) { lower_case_csuffix = "type" + lower_case_csuffix.substring ("type_".length); } else if (lower_case_csuffix.has_prefix ("is_")) { lower_case_csuffix = "is" + lower_case_csuffix.substring ("is_".length); } if (lower_case_csuffix.has_suffix ("_class")) { lower_case_csuffix = lower_case_csuffix.substring (0, lower_case_csuffix.length - "_class".length) + "class"; } } if (sym.parent_symbol != null) { return "%s%s".printf (get_lower_case_cprefix (sym.parent_symbol), lower_case_csuffix); } else { return lower_case_csuffix; } } private string get_lower_case_cprefix (Symbol sym) { if (sym.name == null) { return ""; } string cprefix; cprefix = sym.get_attribute_string ("CCode", "lower_case_cprefix"); if (cprefix == null && (sym is ObjectTypeSymbol || sym is Struct)) { cprefix = sym.get_attribute_string ("CCode", "cprefix"); } if (cprefix != null) { return cprefix; } return get_lower_case_cname (sym) + "_"; } public string get_cprefix (Symbol sym) { if (sym is ObjectTypeSymbol) { return get_cname (sym); } else if (sym is Enum || sym is ErrorDomain) { return "%s_".printf (get_lower_case_cname (sym).ascii_up ()); } else if (sym is Namespace) { if (sym.name != null) { var cprefix = sym.get_attribute_string ("CCode", "cprefix"); if (cprefix != null) { return cprefix; } if (sym.parent_symbol != null) { return "%s%s".printf (get_cprefix (sym.parent_symbol), sym.name); } else { return sym.name; } } else { return ""; } } else if (sym.name != null) { return sym.name; } return ""; } private string[] get_attributes_for_node (IdlNode node) { string name; if (node.type == IdlNodeTypeId.FUNCTION) { name = ((IdlNodeFunction) node).symbol; } else if (node.type == IdlNodeTypeId.SIGNAL) { name = "%s::%s".printf (get_cname (current_data_type), node.name); } else if (node.type == IdlNodeTypeId.PROPERTY) { name = "%s:%s".printf (get_cname (current_data_type), node.name); } else if (node.type == IdlNodeTypeId.FIELD) { name = "%s.%s".printf (get_cname (current_data_type), node.name); } else { name = node.name; } return get_attributes (name); } private void add_symbol_to_container (Symbol container, Symbol sym) { if (container is Class) { unowned Class cl = (Class) container; if (sym is Class) { cl.add_class ((Class) sym); } else if (sym is Constant) { cl.add_constant ((Constant) sym); } else if (sym is Enum) { cl.add_enum ((Enum) sym); } else if (sym is Field) { cl.add_field ((Field) sym); } else if (sym is Method) { cl.add_method ((Method) sym); } else if (sym is Property) { cl.add_property ((Property) sym); } else if (sym is Signal) { cl.add_signal ((Signal) sym); } else if (sym is Struct) { cl.add_struct ((Struct) sym); } } else if (container is Enum) { unowned Enum en = (Enum) container; if (sym is EnumValue) { en.add_value ((EnumValue) sym); } else if (sym is Constant) { en.add_constant ((Constant) sym); } else if (sym is Method) { en.add_method ((Method) sym); } } else if (container is Interface) { unowned Interface iface = (Interface) container; if (sym is Class) { iface.add_class ((Class) sym); } else if (sym is Constant) { iface.add_constant ((Constant) sym); } else if (sym is Enum) { iface.add_enum ((Enum) sym); } else if (sym is Field) { iface.add_field ((Field) sym); } else if (sym is Method) { iface.add_method ((Method) sym); } else if (sym is Property) { iface.add_property ((Property) sym); } else if (sym is Signal) { iface.add_signal ((Signal) sym); } else if (sym is Struct) { iface.add_struct ((Struct) sym); } } else if (container is Namespace) { unowned Namespace ns = (Namespace) container; if (sym is Namespace) { ns.add_namespace ((Namespace) sym); } else if (sym is Class) { ns.add_class ((Class) sym); } else if (sym is Constant) { ns.add_constant ((Constant) sym); } else if (sym is Delegate) { ns.add_delegate ((Delegate) sym); } else if (sym is Enum) { ns.add_enum ((Enum) sym); } else if (sym is ErrorDomain) { ns.add_error_domain ((ErrorDomain) sym); } else if (sym is Field) { unowned Field field = (Field) sym; if (field.binding == MemberBinding.INSTANCE) { field.binding = MemberBinding.STATIC; } ns.add_field (field); } else if (sym is Interface) { ns.add_interface ((Interface) sym); } else if (sym is Method) { unowned Method method = (Method) sym; if (method.binding == MemberBinding.INSTANCE) { method.binding = MemberBinding.STATIC; } ns.add_method (method); } else if (sym is Namespace) { ns.add_namespace ((Namespace) sym); } else if (sym is Struct) { ns.add_struct ((Struct) sym); } } else if (container is Struct) { unowned Struct st = (Struct) container; if (sym is Constant) { st.add_constant ((Constant) sym); } else if (sym is Field) { st.add_field ((Field) sym); } else if (sym is Method) { st.add_method ((Method) sym); } else if (sym is Property) { st.add_property ((Property) sym); } } if (!(sym is Namespace) && container is Namespace) { // set C headers sym.set_attribute_string ("CCode", "cheader_filename", get_cheader_filename (sym)); } } private void parse_node (IdlNode node, IdlModule module, Symbol container) { if (node.type == IdlNodeTypeId.CALLBACK) { var cb = parse_delegate ((IdlNodeFunction) node); if (cb == null) { return; } cb.name = fix_type_name (cb.name, container); add_symbol_to_container (container, cb); current_source_file.add_node (cb); } else if (node.type == IdlNodeTypeId.STRUCT) { parse_struct ((IdlNodeStruct) node, container, module); } else if (node.type == IdlNodeTypeId.UNION) { parse_union ((IdlNodeUnion) node, container, module); } else if (node.type == IdlNodeTypeId.BOXED) { parse_boxed ((IdlNodeBoxed) node, container, module); } else if (node.type == IdlNodeTypeId.ENUM) { parse_enum ((IdlNodeEnum) node, container, module, false); } else if (node.type == IdlNodeTypeId.FLAGS) { parse_enum ((IdlNodeEnum) node, container, module, true); } else if (node.type == IdlNodeTypeId.OBJECT) { parse_object ((IdlNodeInterface) node, container, module); } else if (node.type == IdlNodeTypeId.INTERFACE) { parse_interface ((IdlNodeInterface) node, container, module); } else if (node.type == IdlNodeTypeId.CONSTANT) { var c = parse_constant ((IdlNodeConstant) node); if (c != null) { c.name = fix_const_name (c.name, container); add_symbol_to_container (container, c); current_source_file.add_node (c); } } else if (node.type == IdlNodeTypeId.FUNCTION) { var m = parse_function ((IdlNodeFunction) node); if (m != null) { if (!(m is CreationMethod)) { m.binding = MemberBinding.STATIC; } add_symbol_to_container (container, m); current_source_file.add_node (m); } } } private Symbol? get_container_from_name (string name) { var path = name.split ("."); Symbol? cp = current_namespace; if (cp.parent_symbol != context.root) { cp = cp.parent_symbol; } Symbol? cc = null; foreach ( unowned string tok in path ) { cc = cp.scope.lookup (tok); if ( cc == null ) { cc = new Namespace (tok, current_source_reference); add_symbol_to_container (cp, cc); } cp = cc; } return cc; } private Namespace? parse_module (IdlModule module) { Symbol sym = context.root.scope.lookup (module.name); Namespace ns; if (sym is Namespace) { ns = (Namespace) sym; if (ns.external_package) { ns.attributes = null; ns.source_reference = current_source_reference; } } else { ns = new Namespace (module.name, current_source_reference); } current_namespace = ns; var attributes = get_attributes (ns.name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { ns.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "cprefix") { ns.set_attribute_string ("CCode", "cprefix", eval (nv[1])); } else if (nv[0] == "lower_case_cprefix") { ns.set_attribute_string ("CCode", "lower_case_cprefix", eval (nv[1])); } else if (nv[0] == "gir_namespace") { ns.source_reference.file.gir_namespace = eval (nv[1]); ns.set_attribute_string ("CCode", "gir_namespace", eval (nv[1])); } else if (nv[0] == "gir_version") { ns.source_reference.file.gir_version = eval (nv[1]); ns.set_attribute_string ("CCode", "gir_version", eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { ns.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { ns.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { ns.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } } } var deferred = new ArrayList (); foreach (weak IdlNode node in module.entries) { bool is_deferred = false; var child_attributes = get_attributes_for_node (node); if (child_attributes != null) { foreach (unowned string attr in child_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "parent") { deferred.add (node); is_deferred = true; } } } if (!is_deferred) { parse_node (node, module, ns); } } foreach (unowned IdlNode node in deferred) { Symbol container = ns; var child_attributes = get_attributes_for_node (node); if (child_attributes != null) { foreach (unowned string attr in child_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "parent") { container = get_container_from_name (eval (nv[1])); } } } if (container is Namespace) { current_namespace = (Namespace) container; } else { current_data_type = (TypeSymbol) container; } parse_node (node, module, container); current_namespace = ns; current_data_type = null; } current_namespace = null; if (sym is Namespace) { return null; } return ns; } private Delegate? parse_delegate (IdlNodeFunction f_node) { weak IdlNode node = (IdlNode) f_node; var return_type = parse_param (f_node.result); var cb = new Delegate (node.name, return_type, current_source_reference); cb.access = SymbolAccessibility.PUBLIC; cb.has_target = false; bool check_has_target = true; bool suppress_throws = false; string? error_types = null; var attributes = get_attributes (node.name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } } else if (nv[0] == "cheader_filename") { cb.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "has_target") { if (eval (nv[1]) == "0") { check_has_target = false; } else if (eval (nv[1]) == "1") { cb.has_target = true; } } else if (nv[0] == "transfer_ownership") { if (eval (nv[1]) == "1") { return_type.value_owned = true; } } else if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { return_type.value_owned = true; return_type = new ArrayType (return_type, 1, return_type.source_reference); cb.return_type = return_type; } } else if (nv[0] == "throws") { if (eval (nv[1]) == "0") { suppress_throws = true; } } else if (nv[0] == "error_types") { error_types = eval (nv[1]); } else if (nv[0] == "array_length_type") { cb.set_attribute_string ("CCode", "array_length_type", eval (nv[1])); } else if (nv[0] == "type_name") { cb.return_type = return_type = parse_type_from_string (eval (nv[1]), return_type.value_owned); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { cb.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { cb.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { cb.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (return_type, eval (nv[1])); } else if (nv[0] == "instance_pos") { cb.set_attribute_double ("CCode", "instance_pos", double.parse (eval (nv[1]))); } else if (nv[0] == "type_parameters") { foreach (string type_param_name in eval (nv[1]).split (",")) { cb.add_type_parameter (new TypeParameter (type_param_name, current_source_reference)); } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { cb.set_attribute_bool ("Version", "experimental", true); } } } } uint remaining_params = f_node.parameters.length (); foreach (weak IdlNodeParam param in f_node.parameters) { weak IdlNode param_node = (IdlNode) param; if (check_has_target && remaining_params == 1 && (param_node.name == "user_data" || param_node.name == "data")) { // hide user_data parameter for instance delegates cb.has_target = true; } else { // check for GError parameter if (suppress_throws == false && param_is_exception (param)) { if (error_types == null) cb.add_error_type (parse_type (param.type)); remaining_params--; continue; } string param_name = param_node.name; if (param_name == "string") { // avoid conflict with string type param_name = "str"; } else if (param_name == "self") { // avoid conflict with delegate target param_name = "_self"; } ParameterDirection direction; var param_type = parse_param (param, out direction); var p = new Parameter (param_name, param_type, current_source_reference); p.direction = direction; bool hide_param = false; bool show_param = false; bool array_requested = false; bool out_requested = false; attributes = get_attributes ("%s.%s".printf (node.name, param_node.name)); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { hide_param = true; } else if (eval (nv[1]) == "0") { show_param = true; } } else if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { param_type.value_owned = true; param_type = new ArrayType (param_type, 1, param_type.source_reference); p.variable_type = param_type; if (!out_requested) { p.direction = ParameterDirection.IN; } array_requested = true; } } else if (nv[0] == "is_out") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.OUT; out_requested = true; if (!array_requested && param_type is ArrayType) { var array_type = (ArrayType) param_type; param_type = array_type.element_type; p.variable_type = param_type; } } } else if (nv[0] == "is_ref") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.REF; if (!array_requested && param_type is ArrayType) { var array_type = (ArrayType) param_type; param_type = array_type.element_type; p.variable_type = param_type; } } } else if (nv[0] == "takes_ownership") { if (eval (nv[1]) == "1") { param_type.value_owned = true; } } else if (nv[0] == "nullable") { if (eval (nv[1]) == "1") { param_type.nullable = true; } } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (param_type, eval (nv[1])); } else if (nv[0] == "no_array_length") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); } } else if (nv[0] == "array_length_type") { p.set_attribute_string ("CCode", "array_length_type", eval (nv[1])); } else if (nv[0] == "array_null_terminated") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); p.set_attribute_bool ("CCode", "array_null_terminated", true); } } else if (nv[0] == "type_name") { p.variable_type = param_type = parse_type_from_string (eval (nv[1]), false); } } } if (show_param || !hide_param) { cb.add_parameter (p); } } remaining_params--; } if (suppress_throws == false && error_types != null) { var type_args = eval (error_types).split (","); foreach (unowned string type_arg in type_args) { cb.add_error_type (parse_type_from_string (type_arg, true)); } } return cb; } private bool is_reference_type (string cname) { var st_attributes = get_attributes (cname); if (st_attributes != null) { foreach (string attr in st_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "is_value_type" && eval (nv[1]) == "1") { return false; } } } return true; } private void parse_struct (IdlNodeStruct st_node, Symbol container, IdlModule module) { weak IdlNode node = (IdlNode) st_node; if (st_node.deprecated) { return; } string name = fix_type_name (node.name, container); if (!is_reference_type (node.name)) { var st = container.scope.lookup (name) as Struct; if (st == null) { st = new Struct (name, current_source_reference); st.access = SymbolAccessibility.PUBLIC; var st_attributes = get_attributes (node.name); if (st_attributes != null) { foreach (string attr in st_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { st.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "base_type") { st.base_type = parse_type_string (eval (nv[1])); } else if (nv[0] == "rank") { st.rank = int.parse (eval (nv[1])); } else if (nv[0] == "simple_type") { if (eval (nv[1]) == "1") { st.set_simple_type (true); } } else if (nv[0] == "immutable") { if (eval (nv[1]) == "1") { st.set_attribute ("Immutable", true); } } else if (nv[0] == "has_type_id") { if (eval (nv[1]) == "0") { st.set_attribute_bool ("CCode", "has_type_id", false); } } else if (nv[0] == "type_id") { st.set_attribute_string ("CCode", "type_id", eval (nv[1])); } else if (nv[0] == "has_copy_function") { if (eval (nv[1]) == "0") { st.set_attribute_bool ("CCode", "has_copy_function", false); } } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { st.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { st.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "has_destroy_function") { if (eval (nv[1]) == "0") { st.set_attribute_bool ("CCode", "has_destroy_function", false); } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "experimental", true); } } } } add_symbol_to_container (container, st); current_source_file.add_node (st); } current_data_type = st; foreach (weak IdlNode member in st_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { var m = parse_function ((IdlNodeFunction) member); if (m != null) { st.add_method (m); } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { st.add_field (f); } } } current_data_type = null; } else { bool ref_function_void = false; string ref_function = null; string unref_function = null; string copy_function = null; string free_function = null; var cl = container.scope.lookup (name) as Class; if (cl == null) { string base_class = null; bool is_fundamental = false; cl = new Class (name, current_source_reference); cl.access = SymbolAccessibility.PUBLIC; var cl_attributes = get_attributes (node.name); if (cl_attributes != null) { foreach (string attr in cl_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { cl.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "base_class") { base_class = eval (nv[1]); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "is_immutable") { if (eval (nv[1]) == "1") { cl.set_attribute ("Immutable", true); } } else if (nv[0] == "const_cname") { cl.set_attribute_string ("CCode", "const_cname", eval (nv[1])); } else if (nv[0] == "is_fundamental") { if (eval (nv[1]) == "1") { is_fundamental = true; } } else if (nv[0] == "abstract" && base_class != null) { if (eval (nv[1]) == "1") { cl.is_abstract = true; } } else if (nv[0] == "free_function") { free_function = eval (nv[1]); } else if (nv[0] == "ref_function") { ref_function = eval (nv[1]); } else if (nv[0] == "unref_function") { unref_function = eval (nv[1]); } else if (nv[0] == "copy_function") { copy_function = eval (nv[1]); } else if (nv[0] == "ref_function_void") { if (eval (nv[1]) == "1") { ref_function_void = true; } } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { cl.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { cl.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "type_parameters") { foreach (unowned string type_param_name in eval (nv[1]).split (",")) { cl.add_type_parameter (new TypeParameter (type_param_name, current_source_reference)); } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "experimental", true); } } else if (nv[0] == "delegate_target_cname") { cl.set_attribute_string ("CCode", "delegate_target_cname", eval (nv[1])); } } } add_symbol_to_container (container, cl); current_source_file.add_node (cl); if (base_class != null) { var parent = parse_type_string (base_class); cl.add_base_type (parent); } if (base_class == null && !is_fundamental) { cl.set_attribute ("Compact", true); } } current_data_type = cl; foreach (weak IdlNode member in st_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { if ((ref_function == null) && (member.name == "ref")) { ref_function = ((IdlNodeFunction) member).symbol; ref_function_void = (parse_type (((IdlNodeFunction) member).result.type) is VoidType); } else if ((unref_function == null) && (member.name == "unref")) { unref_function = ((IdlNodeFunction) member).symbol; } else if ((free_function == null) && (member.name == "free" || member.name == "destroy")) { free_function = ((IdlNodeFunction) member).symbol; } else { if ((copy_function == null) && (member.name == "copy")) { copy_function = ((IdlNodeFunction) member).symbol; } var m = parse_function ((IdlNodeFunction) member); if (m != null) { cl.add_method (m); } } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { cl.add_field (f); } } } if (ref_function != null) { cl.set_attribute_string ("CCode", "ref_function", ref_function); if (ref_function_void) { cl.set_attribute_bool ("CCode", "ref_function_void", ref_function_void); } } else if (copy_function != null) { cl.set_attribute_string ("CCode", "copy_function", copy_function); } if (unref_function != null) { cl.set_attribute_string ("CCode", "unref_function", unref_function); } else if (free_function != null && free_function != "%sfree".printf (get_lower_case_cprefix (cl))) { cl.set_attribute_string ("CCode", "free_function", free_function); } current_data_type = null; } } private void parse_union (IdlNodeUnion un_node, Symbol container, IdlModule module) { weak IdlNode node = (IdlNode) un_node; if (un_node.deprecated) { return; } string name = fix_type_name (node.name, container); if (!is_reference_type (node.name)) { var st = container.scope.lookup (name) as Struct; if (st == null) { st = new Struct (name, current_source_reference); st.access = SymbolAccessibility.PUBLIC; var st_attributes = get_attributes (node.name); if (st_attributes != null) { foreach (string attr in st_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { st.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { st.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { st.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "experimental", true); } } } } add_symbol_to_container (container, st); current_source_file.add_node (st); } current_data_type = st; foreach (weak IdlNode member in un_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { var m = parse_function ((IdlNodeFunction) member); if (m != null) { st.add_method (m); } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { st.add_field (f); } } } current_data_type = null; } else { var cl = container.scope.lookup (name) as Class; if (cl == null) { cl = new Class (name, current_source_reference); cl.access = SymbolAccessibility.PUBLIC; cl.set_attribute ("Compact", true); var cl_attributes = get_attributes (node.name); if (cl_attributes != null) { foreach (string attr in cl_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { cl.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } } } add_symbol_to_container (container, cl); current_source_file.add_node (cl); } current_data_type = cl; bool ref_function_void = false; string ref_function = null; string unref_function = null; string copy_function = null; string free_function = null; foreach (weak IdlNode member in un_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { if (member.name == "ref") { ref_function = ((IdlNodeFunction) member).symbol; ref_function_void = (parse_type (((IdlNodeFunction) member).result.type) is VoidType); } else if (member.name == "unref") { unref_function = ((IdlNodeFunction) member).symbol; } else if (member.name == "free" || member.name == "destroy") { free_function = ((IdlNodeFunction) member).symbol; } else { if (member.name == "copy") { copy_function = ((IdlNodeFunction) member).symbol; } var m = parse_function ((IdlNodeFunction) member); if (m != null) { cl.add_method (m); } } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { cl.add_field (f); } } } if (ref_function != null) { cl.set_attribute_string ("CCode", "ref_function", ref_function); if (ref_function_void) { cl.set_attribute_bool ("CCode", "ref_function_void", ref_function_void); } } else if (copy_function != null) { cl.set_attribute_string ("CCode", "copy_function", copy_function); } if (unref_function != null) { cl.set_attribute_string ("CCode", "unref_function", unref_function); } else if (free_function != null && free_function != "%sfree".printf (get_lower_case_cprefix (cl))) { cl.set_attribute_string ("CCode", "free_function", free_function); } current_data_type = null; } } private void parse_boxed (IdlNodeBoxed boxed_node, Symbol container, IdlModule module) { weak IdlNode node = (IdlNode) boxed_node; string name = fix_type_name (node.name, container); var node_attributes = get_attributes (node.name); if (node_attributes != null) { foreach (string attr in node_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { return; } } } if (!is_reference_type (node.name)) { var st = container.scope.lookup (name) as Struct; if (st == null) { st = new Struct (name, current_source_reference); st.access = SymbolAccessibility.PUBLIC; var st_attributes = get_attributes (node.name); if (st_attributes != null) { foreach (string attr in st_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { st.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { st.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { st.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "immutable") { if (eval (nv[1]) == "1") { st.set_attribute ("Immutable", true); } } else if (nv[0] == "has_copy_function") { if (eval (nv[1]) == "0") { st.set_attribute_bool ("CCode", "has_copy_function", false); } } else if (nv[0] == "has_destroy_function") { if (eval (nv[1]) == "0") { st.set_attribute_bool ("CCode", "has_destroy_function", false); } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { st.set_attribute_bool ("Version", "experimental", true); } } } } add_symbol_to_container (container, st); current_source_file.add_node (st); } current_data_type = st; foreach (weak IdlNode member in boxed_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { var m = parse_function ((IdlNodeFunction) member); if (m != null) { st.add_method (m); } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { st.add_field (f); } } } current_data_type = null; } else { bool ref_function_void = false; string ref_function = null; string unref_function = null; string copy_function = null; string free_function = null; var cl = container.scope.lookup (name) as Class; if (cl == null) { string base_class = null; cl = new Class (name, current_source_reference); cl.access = SymbolAccessibility.PUBLIC; cl.set_attribute ("Compact", true); if (boxed_node.gtype_init != null) { cl.set_attribute_string ("CCode", "type_id", "%s ()".printf (boxed_node.gtype_init)); } var cl_attributes = get_attributes (node.name); if (cl_attributes != null) { foreach (string attr in cl_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { cl.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "base_class") { base_class = eval (nv[1]); } else if (nv[0] == "is_immutable") { if (eval (nv[1]) == "1") { cl.set_attribute ("Immutable", true); } } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { cl.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { cl.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "const_cname") { cl.set_attribute_string ("CCode", "const_cname", eval (nv[1])); } else if (nv[0] == "free_function") { free_function = eval (nv[1]); } else if (nv[0] == "ref_function") { ref_function = eval (nv[1]); } else if (nv[0] == "unref_function") { unref_function = eval (nv[1]); } else if (nv[0] == "copy_function") { copy_function = eval (nv[1]); } else if (nv[0] == "ref_function_void") { if (eval (nv[1]) == "1") { ref_function_void = true; } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "experimental", true); } } } } add_symbol_to_container (container, cl); current_source_file.add_node (cl); if (base_class != null) { var parent = parse_type_string (base_class); cl.add_base_type (parent); } } current_data_type = cl; foreach (weak IdlNode member in boxed_node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { if (member.name == "ref") { ref_function = ((IdlNodeFunction) member).symbol; ref_function_void = (parse_type (((IdlNodeFunction) member).result.type) is VoidType); } else if (member.name == "unref") { unref_function = ((IdlNodeFunction) member).symbol; } else if (member.name == "free" || member.name == "destroy") { free_function = ((IdlNodeFunction) member).symbol; } else { if (member.name == "copy") { copy_function = ((IdlNodeFunction) member).symbol; } var m = parse_function ((IdlNodeFunction) member); if (m != null) { cl.add_method (m); } } } else if (member.type == IdlNodeTypeId.FIELD) { var f = parse_field ((IdlNodeField) member); if (f != null) { cl.add_field (f); } } } if (ref_function != null) { cl.set_attribute_string ("CCode", "ref_function", ref_function); if (ref_function_void) { cl.set_attribute_bool ("CCode", "ref_function_void", ref_function_void); } } else if (copy_function != null) { cl.set_attribute_string ("CCode", "copy_function", copy_function); } if (unref_function != null) { cl.set_attribute_string ("CCode", "unref_function", unref_function); } else if (free_function != null && free_function != "%sfree".printf (get_lower_case_cprefix (cl))) { cl.set_attribute_string ("CCode", "free_function", free_function); } current_data_type = null; } } private void parse_enum (IdlNodeEnum en_node, Symbol container, IdlModule module, bool is_flags) { weak IdlNode node = (IdlNode) en_node; string name = fix_type_name (node.name, container); bool existing = true; var en = container.scope.lookup (name) as Enum; if (en == null) { en = new Enum (name, current_source_reference); en.access = SymbolAccessibility.PUBLIC; existing = false; } else { // ignore dummy enum values in -custom.vala files // they exist for syntactical reasons var dummy = (EnumValue) en.scope.lookup ("__DUMMY__"); if (dummy != null) { en.get_values ().remove (dummy); en.scope.remove ("__DUMMY__"); } } if (en_node.gtype_name == null || en_node.gtype_name == "") { en.set_attribute_bool ("CCode", "has_type_id", false); } string common_prefix = null; foreach (weak IdlNode value in en_node.values) { var val_attributes = get_attributes (value.name); bool is_hidden = false; if (val_attributes != null) { foreach (string attr in val_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden" && eval(nv[1]) == "1") { is_hidden = true; } } } if (is_hidden) { continue; } if (common_prefix == null) { common_prefix = value.name; while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) { // FIXME: could easily be made faster common_prefix = common_prefix.substring (0, common_prefix.length - 1); } } else { while (!value.name.has_prefix (common_prefix)) { common_prefix = common_prefix.substring (0, common_prefix.length - 1); } } while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") || (value.name.get_char (common_prefix.length).isdigit ()) && (value.name.length - common_prefix.length) <= 1)) { // enum values may not consist solely of digits common_prefix = common_prefix.substring (0, common_prefix.length - 1); } } bool is_errordomain = false; string cheader_filename = null; var en_attributes = get_attributes (node.name); if (en_attributes != null) { foreach (string attr in en_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "common_prefix") { common_prefix = eval (nv[1]); } else if (nv[0] == "cheader_filename") { cheader_filename = eval (nv[1]); en.set_attribute_string ("CCode", "cheader_filename", cheader_filename); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { en.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "default_value") { en.set_attribute_string ("CCode", "default_value", eval (nv[1])); } else if (nv[0] == "replacement") { en.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { en.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "rename_to") { en.name = eval (nv[1]); } else if (nv[0] == "errordomain") { if (eval (nv[1]) == "1") { is_errordomain = true; } } else if (nv[0] == "to_string") { var return_type = new UnresolvedType (); return_type.unresolved_symbol = new UnresolvedSymbol (null, "string"); return_type.value_owned = false; var m = new Method ("to_string", return_type, current_source_reference); m.access = SymbolAccessibility.PUBLIC; m.set_attribute_string ("CCode", "cname", eval(nv[1])); en.add_method (m); } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { en.set_attribute_bool ("Version", "experimental", true); } } } } en.set_attribute_string ("CCode", "cprefix", common_prefix); foreach (weak IdlNode value2 in en_node.values) { EnumValue ev = new EnumValue (value2.name.substring (common_prefix.length), null); var val_attributes = get_attributes (value2.name); bool is_hidden = false; if (val_attributes != null) { foreach (string attr in val_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden" && eval(nv[1]) == "1") { is_hidden = true; } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { ev.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { ev.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { ev.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } } } if (!is_hidden) { en.add_value (ev); } } if (is_errordomain) { var ed = new ErrorDomain (en.name, current_source_reference); ed.access = SymbolAccessibility.PUBLIC; ed.set_attribute_string ("CCode", "cprefix", common_prefix); if (cheader_filename != null) { ed.set_attribute_string ("CCode", "cheader_filename", cheader_filename); } foreach (EnumValue ev in en.get_values ()) { ed.add_code (new ErrorCode (ev.name)); } current_source_file.add_node (ed); if (!existing) { add_symbol_to_container (container, ed); } } else { en.set_attribute ("Flags", is_flags); current_source_file.add_node (en); if (!existing) { add_symbol_to_container (container, en); } } } private void parse_object (IdlNodeInterface node, Symbol container, IdlModule module) { string name = fix_type_name (((IdlNode) node).name, container); string base_class = null; var cl = container.scope.lookup (name) as Class; if (cl == null) { cl = new Class (name, current_source_reference); cl.access = SymbolAccessibility.PUBLIC; var attributes = get_attributes (node.gtype_name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { cl.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "base_class") { base_class = eval (nv[1]); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "type_check_function") { cl.set_attribute_string ("CCode", "type_check_function", eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { cl.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { cl.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "type_id") { cl.set_attribute_string ("CCode", "type_id", eval (nv[1])); } else if (nv[0] == "abstract") { if (eval (nv[1]) == "1") { cl.is_abstract = true; } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { cl.set_attribute_bool ("Version", "experimental", true); } } else if (nv[0] == "compact") { if (eval (nv[1]) == "1") { cl.set_attribute ("Compact", true); } } else if (nv[0] == "ref_function") { cl.set_attribute_string ("CCode", "ref_function", eval (nv[1])); } else if (nv[0] == "unref_function") { cl.set_attribute_string ("CCode", "unref_function", eval (nv[1])); } else if (nv[0] == "copy_function") { cl.set_attribute_string ("CCode", "copy_function", eval (nv[1])); } else if (nv[0] == "free_function") { cl.set_attribute_string ("CCode", "free_function", eval (nv[1])); } } } add_symbol_to_container (container, cl); current_source_file.add_node (cl); } if (base_class != null) { var parent = parse_type_string (base_class); cl.add_base_type (parent); } else if (node.parent != null) { if (!cl.is_compact) { var parent = parse_type_string (node.parent); cl.add_base_type (parent); } } else { var gobject_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "Object"); cl.add_base_type (new UnresolvedType.from_symbol (gobject_symbol)); } foreach (string iface_name in node.interfaces) { bool skip_iface = false; var attributes = get_attributes (iface_name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { skip_iface = true; } } } } if (skip_iface) { continue; } var iface = parse_type_string (iface_name); cl.add_base_type (iface); } current_data_type = cl; current_type_symbol_set = new HashSet (str_hash, str_equal); var current_type_func_map = new HashMap (str_hash, str_equal); var current_type_vfunc_map = new HashMap (str_hash, str_equal); foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { current_type_func_map.set (member.name, (IdlNodeFunction) member); } if (member.type == IdlNodeTypeId.VFUNC) { current_type_vfunc_map.set (member.name, "1"); } } foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { // Ignore if vfunc (handled below) if (!current_type_vfunc_map.contains (member.name)) { var m = parse_function ((IdlNodeFunction) member); if (m != null) { cl.add_method (m); } } } else if (member.type == IdlNodeTypeId.VFUNC) { var m = parse_virtual ((IdlNodeVFunc) member, current_type_func_map.get (member.name)); if (m != null) { cl.add_method (m); } } else if (member.type == IdlNodeTypeId.PROPERTY) { var prop = parse_property ((IdlNodeProperty) member); if (prop != null) { cl.add_property (prop); } } else if (member.type == IdlNodeTypeId.SIGNAL) { var sig = parse_signal ((IdlNodeSignal) member); if (sig != null) { cl.add_signal (sig); } } } foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FIELD) { if (!current_type_symbol_set.contains (member.name)) { var f = parse_field ((IdlNodeField) member); if (f != null) { cl.add_field (f); } } } } foreach (Property prop in cl.get_properties ()) { var getter = "get_%s".printf (prop.name); if (prop.get_accessor != null && !current_type_symbol_set.contains (getter)) { prop.set_attribute ("NoAccessorMethod", true); } var setter = "set_%s".printf (prop.name); if (prop.set_accessor != null && prop.set_accessor.writable && !current_type_symbol_set.contains (setter)) { prop.set_attribute ("NoAccessorMethod", true); } if (prop.get_attribute ("NoAccessorMethod") != null && prop.get_accessor != null) { prop.get_accessor.value_type.value_owned = true; } } handle_async_methods (cl); if (cl.default_construction_method == null) { // always provide constructor in generated bindings // to indicate that implicit Object () chainup is allowed var cm = new CreationMethod (null, null, cl.source_reference); cm.has_construct_function = false; cm.access = SymbolAccessibility.PROTECTED; cl.add_method (cm); } current_data_type = null; current_type_symbol_set = null; } private void parse_interface (IdlNodeInterface node, Symbol container, IdlModule module) { string name = fix_type_name (node.gtype_name, container); var iface = container.scope.lookup (name) as Interface; if (iface == null) { iface = new Interface (name, current_source_reference); iface.access = SymbolAccessibility.PUBLIC; var attributes = get_attributes (node.gtype_name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { iface.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return; } } else if (nv[0] == "type_cname") { iface.set_attribute_string ("CCode", "type_cname", eval (nv[1])); } else if (nv[0] == "lower_case_csuffix") { iface.set_attribute_string ("CCode", "lower_case_csuffix", eval (nv[1])); } } } foreach (string prereq_name in node.prerequisites) { var prereq = parse_type_string (prereq_name); iface.add_prerequisite (prereq); } add_symbol_to_container (container, iface); current_source_file.add_node (iface); } current_data_type = iface; current_type_symbol_set = new HashSet (str_hash, str_equal); var current_type_func_map = new HashMap (str_hash, str_equal); var current_type_vfunc_map = new HashMap (str_hash, str_equal); foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { current_type_func_map.set (member.name, (IdlNodeFunction) member); } if (member.type == IdlNodeTypeId.VFUNC) { current_type_vfunc_map.set (member.name, "1"); } } foreach (weak IdlNode member in node.members) { if (member.type == IdlNodeTypeId.FUNCTION) { // Ignore if vfunc (handled below) if (!current_type_vfunc_map.contains (member.name)) { var m = parse_function ((IdlNodeFunction) member, true); if (m != null) { iface.add_method (m); } } } else if (member.type == IdlNodeTypeId.VFUNC) { var m = parse_virtual ((IdlNodeVFunc) member, current_type_func_map.get (member.name), true); if (m != null) { iface.add_method (m); } } else if (member.type == IdlNodeTypeId.PROPERTY) { var prop = parse_property ((IdlNodeProperty) member); if (prop != null) { iface.add_property (prop); } } else if (member.type == IdlNodeTypeId.SIGNAL) { var sig = parse_signal ((IdlNodeSignal) member); if (sig != null) { iface.add_signal (sig); sig.is_virtual = false; } } } foreach (Property prop in iface.get_properties ()) { var getter = "get_%s".printf (prop.name); if (prop.get_accessor != null && !current_type_symbol_set.contains (getter)) { prop.set_attribute ("NoAccessorMethod", true); } var setter = "set_%s".printf (prop.name); if (prop.set_accessor != null && prop.set_accessor.writable && !current_type_symbol_set.contains (setter)) { prop.set_attribute ("NoAccessorMethod", true); } if (prop.get_attribute ("NoAccessorMethod") != null && prop.get_accessor != null) { prop.get_accessor.value_type.value_owned = true; } } handle_async_methods (iface); current_data_type = null; } void handle_async_methods (ObjectTypeSymbol type_symbol) { Set finish_methods = new HashSet (); var methods = type_symbol.get_methods (); foreach (Method m in methods) { if (m.coroutine) { string finish_method_base; if (m.name.has_suffix ("_async")) { finish_method_base = m.name.substring (0, m.name.length - "_async".length); } else { finish_method_base = m.name; } var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method; // check if the method is using non-standard finish method name if (finish_method == null) { var method_cname = get_finish_cname (m); foreach (Method method in type_symbol.get_methods ()) { if (get_cname (method) == method_cname) { finish_method = method; break; } } } if (finish_method != null) { m.return_type = finish_method.return_type.copy (); var a = finish_method.get_attribute ("CCode"); if (a != null && a.has_argument ("array_length")) { m.set_attribute_bool ("CCode", "array_length", a.get_bool ("array_length")); } if (a != null && a.has_argument ("array_null_terminated")) { m.set_attribute_bool ("CCode", "array_null_terminated", a.get_bool ("array_null_terminated")); } foreach (var param in finish_method.get_parameters ()) { if (param.direction == ParameterDirection.OUT) { var async_param = param.copy (); if (m.scope.lookup (param.name) != null) { // parameter name conflict async_param.name += "_out"; } m.add_parameter (async_param); } } var error_types = new ArrayList (); finish_method.get_error_types (error_types, m.source_reference); foreach (DataType error_type in error_types) { m.add_error_type (error_type); } finish_methods.add (finish_method); } } } foreach (Method m in finish_methods) { type_symbol.scope.remove (m.name); methods.remove (m); } } private DataType? parse_type (IdlNodeType type_node, out ParameterDirection direction = null) { direction = ParameterDirection.IN; var type = new UnresolvedType (); if (type_node.tag == TypeTag.VOID) { if (type_node.is_pointer) { return new PointerType (new VoidType ()); } else { return new VoidType (); } } else if (type_node.tag == TypeTag.BOOLEAN) { type.unresolved_symbol = new UnresolvedSymbol (null, "bool"); } else if (type_node.tag == TypeTag.INT8) { type.unresolved_symbol = new UnresolvedSymbol (null, "char"); } else if (type_node.tag == TypeTag.UINT8) { type.unresolved_symbol = new UnresolvedSymbol (null, "uchar"); } else if (type_node.tag == TypeTag.INT16) { type.unresolved_symbol = new UnresolvedSymbol (null, "int16"); } else if (type_node.tag == TypeTag.UINT16) { type.unresolved_symbol = new UnresolvedSymbol (null, "uint16"); } else if (type_node.tag == TypeTag.INT32) { type.unresolved_symbol = new UnresolvedSymbol (null, "int32"); } else if (type_node.tag == TypeTag.UINT32) { type.unresolved_symbol = new UnresolvedSymbol (null, "uint32"); } else if (type_node.tag == TypeTag.INT64) { type.unresolved_symbol = new UnresolvedSymbol (null, "int64"); } else if (type_node.tag == TypeTag.UINT64) { type.unresolved_symbol = new UnresolvedSymbol (null, "uint64"); } else if (type_node.tag == TypeTag.INT) { type.unresolved_symbol = new UnresolvedSymbol (null, "int"); } else if (type_node.tag == TypeTag.UINT) { type.unresolved_symbol = new UnresolvedSymbol (null, "uint"); } else if (type_node.tag == TypeTag.LONG) { type.unresolved_symbol = new UnresolvedSymbol (null, "long"); } else if (type_node.tag == TypeTag.ULONG) { type.unresolved_symbol = new UnresolvedSymbol (null, "ulong"); } else if (type_node.tag == TypeTag.SSIZE) { type.unresolved_symbol = new UnresolvedSymbol (null, "ssize_t"); } else if (type_node.tag == TypeTag.SIZE) { type.unresolved_symbol = new UnresolvedSymbol (null, "size_t"); } else if (type_node.tag == TypeTag.FLOAT) { type.unresolved_symbol = new UnresolvedSymbol (null, "float"); } else if (type_node.tag == TypeTag.DOUBLE) { type.unresolved_symbol = new UnresolvedSymbol (null, "double"); } else if (type_node.tag == TypeTag.UTF8) { type.unresolved_symbol = new UnresolvedSymbol (null, "string"); } else if (type_node.tag == TypeTag.FILENAME) { type.unresolved_symbol = new UnresolvedSymbol (null, "string"); } else if (type_node.tag == TypeTag.ARRAY) { var element_type = parse_type (type_node.parameter_type1); type = element_type as UnresolvedType; if (type == null) { return element_type; } element_type.value_owned = true; return new ArrayType (element_type, 1, element_type.source_reference); } else if (type_node.tag == TypeTag.LIST) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "List"); } else if (type_node.tag == TypeTag.SLIST) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "SList"); } else if (type_node.tag == TypeTag.HASH) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "HashTable"); } else if (type_node.tag == TypeTag.ERROR) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "Error"); } else if (type_node.unparsed.has_prefix ("cairo_device_t") || type_node.unparsed.has_prefix ("cairo_pattern_t") || type_node.unparsed.has_prefix ("cairo_surface_t")) { if (type_node.unparsed.has_prefix ("cairo_device_t")) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "Cairo"), "Device"); } else if (type_node.unparsed.has_prefix ("cairo_pattern_t")) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "Cairo"), "Pattern"); } else if (type_node.unparsed.has_prefix ("cairo_surface_t")) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "Cairo"), "Surface"); } if (type_node.unparsed.has_suffix ("**")) { direction = ParameterDirection.OUT; } } else if (type_node.is_interface) { var n = type_node.@interface; if (n == "") { return null; } if (n.has_prefix ("const-")) { n = n.substring ("const-".length); } if (type_node.is_pointer && (n == "gchar" || n == "char")) { type.unresolved_symbol = new UnresolvedSymbol (null, "string"); if (type_node.unparsed.has_suffix ("**")) { direction = ParameterDirection.OUT; } } else if (n == "gunichar") { type.unresolved_symbol = new UnresolvedSymbol (null, "unichar"); } else if (n == "gchar") { type.unresolved_symbol = new UnresolvedSymbol (null, "char"); } else if (n == "guchar" || n == "guint8") { type.unresolved_symbol = new UnresolvedSymbol (null, "uchar"); if (type_node.is_pointer) { type.value_owned = true; return new ArrayType (type, 1, type.source_reference); } } else if (n == "gushort") { type.unresolved_symbol = new UnresolvedSymbol (null, "ushort"); } else if (n == "gshort") { type.unresolved_symbol = new UnresolvedSymbol (null, "short"); } else if (n == "gconstpointer" || n == "void") { return new PointerType (new VoidType ()); } else if (n == "goffset" || n == "off_t") { type.unresolved_symbol = new UnresolvedSymbol (null, "int64"); } else if (n == "value_array") { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "ValueArray"); } else if (n == "time_t") { type.unresolved_symbol = new UnresolvedSymbol (null, "time_t"); } else if (n == "socklen_t") { type.unresolved_symbol = new UnresolvedSymbol (null, "uint32"); } else if (n == "mode_t") { type.unresolved_symbol = new UnresolvedSymbol (null, "uint"); } else if (n == "gint" || n == "pid_t") { type.unresolved_symbol = new UnresolvedSymbol (null, "int"); } else if (n == "unsigned" || n == "unsigned-int") { type.unresolved_symbol = new UnresolvedSymbol (null, "uint"); } else if (n == "FILE") { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "FileStream"); } else if (n == "struct") { return new PointerType (new VoidType ()); } else if (n == "iconv_t") { return new PointerType (new VoidType ()); } else if (n == "GType") { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), "Type"); if (type_node.is_pointer) { type.value_owned = true; return new ArrayType (type, 1, type.source_reference); } } else if (n == "GStrv") { type.unresolved_symbol = new UnresolvedSymbol (null, "string"); type.value_owned = true; return new ArrayType (type, 1, type.source_reference); } else { var named_type = parse_type_string (n); type = named_type as UnresolvedType; if (type == null) { return named_type; } if (is_simple_type (n)) { if (type_node.is_pointer) { direction = ParameterDirection.OUT; } } else if (type_node.unparsed.has_suffix ("**")) { direction = ParameterDirection.OUT; } } } else { print ("%d\n", type_node.tag); } return type; } private bool is_simple_type (string type_name) { var st = cname_type_map[type_name] as Struct; if (st != null && st.is_simple_type ()) { return true; } return false; } private DataType parse_type_string (string n) { if (n == "va_list") { // unsupported return new PointerType (new VoidType ()); } var type = new UnresolvedType (); var dt = cname_type_map[n]; if (dt != null) { UnresolvedSymbol parent_symbol = null; if (dt.parent_symbol.name != null) { parent_symbol = new UnresolvedSymbol (null, dt.parent_symbol.name); } type.unresolved_symbol = new UnresolvedSymbol (parent_symbol, dt.name); return type; } var type_attributes = get_attributes (n); string ns_name = null; if (null != type_attributes) { foreach (string attr in type_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cprefix") { type.unresolved_symbol = new UnresolvedSymbol (null, n.substring (eval (nv[1]).length)); } else if (nv[0] == "name") { type.unresolved_symbol = new UnresolvedSymbol (null, eval (nv[1])); } else if (nv[0] == "namespace") { ns_name = eval (nv[1]); } else if (nv[0] == "rename_to") { type.unresolved_symbol = new UnresolvedSymbol (null, eval (nv[1])); } } } if (type.unresolved_symbol != null) { if (type.unresolved_symbol.name == "pointer") { return new PointerType (new VoidType ()); } if (ns_name != null) { type.unresolved_symbol.inner = new UnresolvedSymbol (null, ns_name); } return type; } if (n.has_prefix (current_namespace.name)) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, current_namespace.name), n.substring (current_namespace.name.length)); } else if (current_namespace.parent_symbol != null && current_namespace.parent_symbol.name != null && n.has_prefix (current_namespace.parent_symbol.name)) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, current_namespace.parent_symbol.name), n.substring (current_namespace.parent_symbol.name.length)); } else if (n.has_prefix ("G")) { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, "GLib"), n.substring (1)); } else { var name_parts = n.split (".", 2); if (name_parts[1] == null) { type.unresolved_symbol = new UnresolvedSymbol (null, name_parts[0]); } else { type.unresolved_symbol = new UnresolvedSymbol (new UnresolvedSymbol (null, name_parts[0]), name_parts[1]); } } return type; } private DataType? parse_param (IdlNodeParam param, out ParameterDirection direction = null) { var type = parse_type (param.type, out direction); // disable for now as null_ok not yet correctly set // type.non_null = !param.null_ok; return type; } private UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) { UnresolvedSymbol? sym = null; foreach (unowned string s in symbol_string.split (".")) { sym = new UnresolvedSymbol (sym, s, source_reference); } if (sym == null) { Report.error (source_reference, "a symbol must be specified"); } return sym; } private bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) { int type_arguments_length = (int) type_arguments.length; GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length); int depth = 0; for (var c = 0 ; c < type_arguments_length ; c++) { if (type_arguments[c] == '<' || type_arguments[c] == '[') { depth++; current.append_unichar (type_arguments[c]); } else if (type_arguments[c] == '>' || type_arguments[c] == ']') { depth--; current.append_unichar (type_arguments[c]); } else if (type_arguments[c] == ',') { if (depth == 0) { var dt = parse_type_from_string (current.str, true, source_reference); if (dt == null) { return false; } parent_type.add_type_argument (dt); current.truncate (); } else { current.append_unichar (type_arguments[c]); } } else { current.append_unichar (type_arguments[c]); } } var dt = parse_type_from_string (current.str, true, source_reference); if (dt == null) { return false; } parent_type.add_type_argument (dt); return true; } private DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) { if (type_from_string_regex == null) { try { type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[(,*)?\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE); } catch (GLib.RegexError e) { GLib.error ("Unable to compile regex: %s", e.message); } } GLib.MatchInfo match; if (!type_from_string_regex.match (type_string, 0, out match)) { Report.error (source_reference, "unable to parse type"); return null; } DataType? type = null; var ownership_data = match.fetch (1); var type_name = match.fetch (2); var type_arguments_data = match.fetch (3); var pointers_data = match.fetch (4); var array_data = match.fetch (5); var array_dimension_data = match.fetch (6); var nullable_data = match.fetch (7); var nullable = nullable_data != null && nullable_data.length > 0; if (ownership_data == null && type_name == "void") { if (array_data == null && !nullable) { type = new VoidType (source_reference); if (pointers_data != null) { for (int i=0; i < pointers_data.length; i++) { type = new PointerType (type); } } return type; } else { Report.error (source_reference, "invalid void type"); return null; } } bool value_owned = owned_by_default; if (ownership_data == "owned") { value_owned = true; } else if (ownership_data == "unowned") { value_owned = false; } var sym = parse_symbol_from_string (type_name, source_reference); if (sym == null) { return null; } type = new UnresolvedType.from_symbol (sym, source_reference); if (type_arguments_data != null && type_arguments_data.length > 0) { if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) { return null; } } if (pointers_data != null) { for (int i=0; i < pointers_data.length; i++) { type = new PointerType (type); } } if (array_data != null && array_data.length > 0) { type.value_owned = true; type = new ArrayType (type, array_dimension_data.length + 1, source_reference); } type.nullable = nullable; type.value_owned = value_owned; return type; } private Method? create_method (string name, string symbol, IdlNodeParam? res, GLib.List? parameters, bool is_constructor, bool is_interface) { DataType return_type = null; if (res != null) { return_type = parse_param (res); } Method m; if (!is_interface && (is_constructor || name.has_prefix ("new"))) { m = new CreationMethod (null, name, current_source_reference); m.has_construct_function = false; if (m.name == "new") { m.name = null; } else if (m.name.has_prefix ("new_")) { m.name = m.name.substring ("new_".length); } // For classes, check whether a creation method return type equals to the // type of the class created. If the types do not match (e.g. in most // gtk widgets) add an attribute to the creation method indicating the used // return type. if (current_data_type is Class && res != null) { if ("%s*".printf (get_cname (current_data_type)) != res.type.unparsed) { m.set_attribute_string ("CCode", "type", res.type.unparsed); } } } else { m = new Method (name, return_type, current_source_reference); } m.access = SymbolAccessibility.PUBLIC; if (current_type_symbol_set != null) { current_type_symbol_set.add (name); } if (current_data_type != null) { var sig_attributes = get_attributes ("%s::%s".printf (get_cname (current_data_type), name)); if (sig_attributes != null) { foreach (string attr in sig_attributes) { var nv = attr.split ("=", 2); if (nv[0] == "has_emitter" && eval (nv[1]) == "1") { return null; } } } } bool add_ellipsis = false; bool suppress_throws = false; string? error_types = null; Symbol? container = null; var attributes = get_attributes (symbol); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "name") { m.name = eval (nv[1]); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } } else if (nv[0] == "ellipsis") { if (eval (nv[1]) == "1") { add_ellipsis = true; } } else if (nv[0] == "printf_format") { if (eval (nv[1]) == "1") { m.set_attribute ("PrintfFormat", true); } } else if (nv[0] == "transfer_ownership") { if (eval (nv[1]) == "1") { return_type.value_owned = true; } } else if (nv[0] == "transfer_container") { if (eval (nv[1]) == "1") { return_type.value_owned = true; if (return_type is ArrayType) { ((ArrayType) return_type).element_type.value_owned = false; } } } else if (nv[0] == "destroys_instance") { if (eval (nv[1]) == "1") { m.set_attribute ("DestroysInstance", true, m.source_reference); } } else if (nv[0] == "returns_floating_reference") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("CCode", "returns_floating_reference", true); m.return_type.value_owned = true; } } else if (nv[0] == "nullable") { if (eval (nv[1]) == "1" && !(return_type is VoidType)) { return_type.nullable = true; } } else if (nv[0] == "sentinel") { m.set_attribute_string ("CCode", "sentinel", eval (nv[1])); } else if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { return_type.value_owned = true; return_type = new ArrayType (return_type, 1, return_type.source_reference); m.return_type = return_type; } } else if (nv[0] == "is_pointer") { if (eval (nv[1]) == "1") { return_type = new PointerType (return_type, return_type.source_reference); m.return_type = return_type; } } else if (nv[0] == "throws") { if (eval (nv[1]) == "0") { suppress_throws = true; } } else if (nv[0] == "error_types") { error_types = eval (nv[1]); } else if (nv[0] == "no_array_length") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("CCode", "array_length", false); } } else if (nv[0] == "array_null_terminated") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("CCode", "array_length", false); m.set_attribute_bool ("CCode", "array_null_terminated", true);; } } else if (nv[0] == "array_length_type") { m.set_attribute_string ("CCode", "array_length_type", eval (nv[1])); } else if (nv[0] == "type_name") { m.return_type = return_type = parse_type_from_string (eval (nv[1]), return_type.value_owned); } else if (nv[0] == "ctype") { m.set_attribute_string ("CCode", "type", eval (nv[1])); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (return_type, eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { m.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { m.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "cheader_filename") { m.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "abstract") { if (eval (nv[1]) == "1") { m.is_abstract = true; } } else if (nv[0] == "virtual") { if (eval (nv[1]) == "1") { m.is_virtual = true; } } else if (nv[0] == "vfunc_name") { m.set_attribute_string ("CCode", "vfunc_name", eval (nv[1])); } else if (nv[0] == "finish_vfunc_name") { m.set_attribute_string ("CCode", "finish_vfunc_name", eval (nv[1])); } else if (nv[0] == "finish_name") { m.set_attribute_string ("CCode", "finish_name", eval (nv[1])); } else if (nv[0] == "async") { if (eval (nv[1]) == "1") { // force async function, even if it doesn't end in _async m.coroutine = true; } } else if (nv[0] == "parent") { container = get_container_from_name (eval (nv[1])); var prefix = get_lower_case_cprefix (container); if (symbol.has_prefix (prefix)) { m.name = symbol.substring (prefix.length); } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("Version", "experimental", true); } } else if (nv[0] == "simple_generics") { if (eval (nv[1]) == "1") { m.set_attribute_bool ("CCode", "simple_generics", true); } } } } bool first = true; Parameter last_param = null; DataType last_param_type = null; foreach (weak IdlNodeParam param in parameters) { weak IdlNode param_node = (IdlNode) param; if (first) { first = false; if (!(m is CreationMethod) && current_data_type != null && param.type.is_interface && (param_node.name == "self" || param.type.@interface.has_suffix (get_cname (current_data_type)))) { // instance method continue; } else if (!(m is CreationMethod) && current_data_type != null && param.type.is_interface && (param_node.name == "klass" || param.type.@interface.has_suffix ("%sClass".printf(get_cname (current_data_type))))) { // class method m.binding = MemberBinding.CLASS; if (m.name.has_prefix ("class_")) { m.name = m.name.substring ("class_".length, m.name.length - "class_".length); } continue; } else if (!(m is CreationMethod)) { // static method m.binding = MemberBinding.STATIC; } } if (param.type.@interface == "GAsyncReadyCallback" && (symbol.has_suffix ("_async") || m.coroutine)) { // async method m.coroutine = true; continue; } // check for GError parameter if (suppress_throws == false && param_is_exception (param)) { if (error_types == null) m.add_error_type (parse_type (param.type)); continue; } string param_name = param_node.name; if (param_name == "result") { // avoid conflict with generated result variable param_name = "_result"; } else if (param_name == "string") { // avoid conflict with string type param_name = "str"; } ParameterDirection direction; var param_type = parse_param (param, out direction); var p = new Parameter (param_name, param_type, current_source_reference); p.direction = direction; bool hide_param = false; bool show_param = false; bool set_array_length_pos = false; double array_length_pos = 0; bool set_delegate_target_pos = false; double delegate_target_pos = 0; bool array_requested = false; bool out_requested = false; attributes = get_attributes ("%s.%s".printf (symbol, param_node.name)); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { param_type.value_owned = true; param_type = new ArrayType (param_type, 1, param_type.source_reference); p.variable_type = param_type; if (!out_requested) { p.direction = ParameterDirection.IN; } array_requested = true; } } else if (nv[0] == "is_pointer") { if (eval (nv[1]) == "1") { param_type = new PointerType (param_type, return_type.source_reference); p.variable_type = param_type; if (!out_requested) { p.direction = ParameterDirection.IN; } } } else if (nv[0] == "is_out") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.OUT; out_requested = true; if (!array_requested && param_type is ArrayType) { var array_type = (ArrayType) param_type; param_type = array_type.element_type; p.variable_type = param_type; } } } else if (nv[0] == "is_ref") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.REF; if (!array_requested && param_type is ArrayType) { var array_type = (ArrayType) param_type; param_type = array_type.element_type; p.variable_type = param_type; } } } else if (nv[0] == "nullable") { if (eval (nv[1]) == "1" && !(param_type is VoidType)) { param_type.nullable = true; } } else if (nv[0] == "transfer_ownership") { if (eval (nv[1]) == "1") { param_type.value_owned = true; } } else if (nv[0] == "takes_ownership") { if (eval (nv[1]) == "1") { param_type.value_owned = true; } } else if (nv[0] == "value_owned") { if (eval (nv[1]) == "0") { param_type.value_owned = false; } else if (eval (nv[1]) == "1") { param_type.value_owned = true; } } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { hide_param = true; } else if (eval (nv[1]) == "0") { show_param = true; } } else if (nv[0] == "no_array_length") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); } } else if (nv[0] == "array_length_type") { p.set_attribute_string ("CCode", "array_length_type", eval (nv[1])); } else if (nv[0] == "array_null_terminated") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); p.set_attribute_bool ("CCode", "array_null_terminated", true); } } else if (nv[0] == "array_length_pos") { set_array_length_pos = true; array_length_pos = double.parse (eval (nv[1])); } else if (nv[0] == "delegate_target_pos") { set_delegate_target_pos = true; delegate_target_pos = double.parse (eval (nv[1])); } else if (nv[0] == "type_name") { p.variable_type = param_type = parse_type_from_string (eval (nv[1]), false); } else if (nv[0] == "ctype") { p.set_attribute_string ("CCode", "type", eval (nv[1])); } else if (nv[0] == "scope") { p.set_attribute_string ("CCode", "scope", eval (nv[1])); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (param_type, eval (nv[1])); } else if (nv[0] == "default_value") { var val = eval (nv[1]); if (val == "null") { p.initializer = new NullLiteral (param_type.source_reference); } else if (val == "true") { p.initializer = new BooleanLiteral (true, param_type.source_reference); } else if (val == "false") { p.initializer = new BooleanLiteral (false, param_type.source_reference); } else if (val == "") { p.initializer = new StringLiteral ("\"\"", param_type.source_reference); } else { if (int64.try_parse (val)) { p.initializer = new IntegerLiteral (val, param_type.source_reference); } else { if (double.try_parse (val)) { p.initializer = new RealLiteral (val, param_type.source_reference); } else { if (val.has_prefix ("\"") && val.has_suffix ("\"")) { p.initializer = new StringLiteral (val, param_type.source_reference); } else { foreach (unowned string member in val.split (".")) { p.initializer = new MemberAccess (p.initializer, member, param_type.source_reference); } } } } } } } } if (last_param != null && p.name == "n_" + last_param.name) { if (!(last_param_type is ArrayType)) { // last_param is array, p is array length last_param_type.value_owned = true; last_param_type = new ArrayType (last_param_type, 1, last_param_type.source_reference); last_param.variable_type = last_param_type; last_param.direction = ParameterDirection.IN; } // hide array length param hide_param = true; } else if (last_param != null && p.name == "user_data") { // last_param is delegate // hide deleate target param hide_param = true; } if (show_param || !hide_param) { m.add_parameter (p); if (set_array_length_pos) { p.set_attribute_double ("CCode", "array_length_pos", array_length_pos); } if (set_delegate_target_pos) { p.set_attribute_double ("CCode", "delegate_target_pos", delegate_target_pos); } } last_param = p; last_param_type = param_type; } if (suppress_throws == false && error_types != null) { var type_args = eval (error_types).split (","); foreach (unowned string type_arg in type_args) { m.add_error_type (parse_type_from_string (type_arg, true)); } } if (first && !(m is CreationMethod)) { // no parameters => static method m.binding = MemberBinding.STATIC; } if (last_param != null && last_param.name.has_prefix ("first_")) { last_param.ellipsis = true; } else if (add_ellipsis) { m.add_parameter (new Parameter.with_ellipsis (current_source_reference)); } if (container == null) { container = current_data_type; if (container == null) { container = current_namespace; } } if (symbol != get_cname (m, container)) { m.set_attribute_string ("CCode", "cname", symbol); } return m; } private bool param_is_exception (IdlNodeParam param) { if (!param.type.is_error) { return false; } var s = param.type.unparsed.chomp (); if (s.has_suffix ("**")) { return true; } return false; } private Method? parse_function (IdlNodeFunction f, bool is_interface = false) { weak IdlNode node = (IdlNode) f; if (f.deprecated) { return null; } return create_method (node.name, f.symbol, f.result, f.parameters, f.is_constructor, is_interface); } private Method parse_virtual (IdlNodeVFunc v, IdlNodeFunction? func, bool is_interface = false) { weak IdlNode node = (IdlNode) v; string symbol = "%s%s".printf (get_lower_case_cprefix (current_data_type), node.name); if (func != null) { symbol = func.symbol; } Method m = create_method (node.name, symbol, v.result, func != null ? func.parameters : v.parameters, false, is_interface); if (m != null) { m.binding = MemberBinding.INSTANCE; m.is_virtual = !(m.is_abstract || is_interface); m.is_abstract = m.is_abstract || is_interface; var attributes = get_attributes (symbol); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "virtual") { if (eval (nv[1]) == "0") { m.is_virtual = false; m.is_abstract = false; } else { m.is_virtual = true; m.is_abstract = false; } } } } if (func == null) { m.set_attribute ("NoWrapper", true); } } return m; } private string fix_prop_name (string name) { var str = new StringBuilder (); string i = name; while (i.length > 0) { unichar c = i.get_char (); if (c == '-') { str.append_c ('_'); } else { str.append_unichar (c); } i = i.next_char (); } return str.str; } private Property? parse_property (IdlNodeProperty prop_node) { weak IdlNode node = (IdlNode) prop_node; if (prop_node.deprecated) { return null; } if (!prop_node.readable && !prop_node.writable) { // buggy GIDL definition prop_node.readable = true; prop_node.writable = true; } var prop = new Property (fix_prop_name (node.name), parse_type (prop_node.type), null, null, current_source_reference); prop.access = SymbolAccessibility.PUBLIC; prop.interface_only = true; if (prop_node.type.is_interface && prop_node.type.interface == "GStrv") { prop.set_attribute_bool ("CCode", "array_length", false); prop.set_attribute_bool ("CCode", "array_null_terminated", true); } if (prop_node.readable) { prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null); } if (prop_node.writable) { if (prop_node.construct_only) { prop.set_accessor = new PropertyAccessor (false, false, true, prop.property_type.copy (), null, null); } else { prop.set_accessor = new PropertyAccessor (false, true, prop_node.@construct, prop.property_type.copy (), null, null); } } // there is no information about the internal ownership so assume `owned` as default prop.property_type.value_owned = true; var attributes = get_attributes ("%s:%s".printf (get_cname (current_data_type), node.name)); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (prop.property_type, eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { prop.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { prop.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { prop.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "accessor_method") { if (eval (nv[1]) == "0") { prop.set_attribute ("NoAccessorMethod", true); } } else if (nv[0] == "owned_get") { if (eval (nv[1]) == "1") { prop.get_accessor.value_type.value_owned = true; } } else if (nv[0] == "type_name") { prop.property_type = parse_type_from_string (eval (nv[1]), false); } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { prop.set_attribute_bool ("Version", "experimental", true); } } else if (nv[0] == "nullable") { if (eval (nv[1]) == "1" && !(prop.property_type is VoidType)) { prop.property_type.nullable = true; } } else if (nv[0] == "abstract") { if (eval (nv[1]) == "1") { prop.is_abstract = true; } } } } if (current_type_symbol_set != null) { current_type_symbol_set.add (prop.name); } return prop; } private Constant? parse_constant (IdlNodeConstant const_node) { weak IdlNode node = (IdlNode) const_node; var type = parse_type (const_node.type); if (type == null) { return null; } var c = new Constant (node.name, type, null, current_source_reference); c.external = true; string[] attributes = get_attributes (node.name); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "cheader_filename") { c.set_attribute_string ("CCode", "cheader_filename", eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { c.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { c.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { c.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { c.set_attribute_bool ("Version", "experimental", true); } } } } c.access = SymbolAccessibility.PUBLIC; return c; } private Field? parse_field (IdlNodeField field_node) { weak IdlNode node = (IdlNode) field_node; bool unhidden = false; var type = parse_type (field_node.type); if (type == null) { return null; } string cheader_filename = null; string ctype = null; string array_length_cname = null; string array_length_type = null; bool array_null_terminated = false; bool deprecated = false; string deprecated_since = null; string replacement = null; bool experimental = false; bool no_delegate_target = false; var attributes = get_attributes ("%s.%s".printf (get_cname (current_data_type), node.name)); if (attributes != null) { foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } else { unhidden = true; } } else if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { type.value_owned = true; type = new ArrayType (type, 1, type.source_reference); } } else if (nv[0] == "weak") { if (eval (nv[1]) == "0") { type.value_owned = true; } } else if (nv[0] == "value_owned") { if (eval (nv[1]) == "0") { type.value_owned = false; } else if (eval (nv[1]) == "1") { type.value_owned = true; } } else if (nv[0] == "type_name") { type = parse_type_from_string (eval (nv[1]), true); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (type, eval (nv[1])); } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { deprecated = true; } } else if (nv[0] == "replacement") { replacement = eval (nv[1]); } else if (nv[0] == "deprecated_since") { deprecated_since = eval (nv[1]); } else if (nv[0] == "cheader_filename") { cheader_filename = eval (nv[1]); } else if (nv[0] == "ctype") { ctype = eval (nv[1]); } else if (nv[0] == "array_null_terminated") { if (eval (nv[1]) == "1") { array_null_terminated = true; } } else if (nv[0] == "array_length_cname") { array_length_cname = eval (nv[1]); } else if (nv[0] == "array_length_type") { array_length_type = eval (nv[1]); } else if (nv[0] == "no_delegate_target") { if (eval (nv[1]) == "1") { no_delegate_target = true; } } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { experimental = true; } } } } if (node.name.has_prefix("_") && !unhidden) { return null; } if (current_type_symbol_set != null) { current_type_symbol_set.add (node.name); } string field_name = node.name; if (field_name == "string") { // avoid conflict with string type field_name = "str"; } var field = new Field (field_name, type, null, current_source_reference); field.access = SymbolAccessibility.PUBLIC; if (field_name != node.name) { field.set_attribute_string ("CCode", "cname", node.name); } if (deprecated) { field.set_attribute_bool ("Version", "deprecated", true); if (deprecated_since != null) { field.set_attribute_string ("Version", "deprecated_since", deprecated_since); } if (replacement != null) { field.set_attribute_string ("Version", "replacement", replacement); } } if (experimental) { field.set_attribute_bool ("Version", "experimental", true); } if (ctype != null) { field.set_attribute_string ("CCode", "type", ctype); } if (cheader_filename != null) { field.set_attribute_string ("CCode", "cheader_filename", cheader_filename); } if (array_null_terminated) { field.set_attribute_bool ("CCode", "array_null_terminated", true); } if (array_length_cname != null || array_length_type != null) { if (array_length_cname != null) { field.set_attribute_string ("CCode", "array_length_cname", array_length_cname); } if (array_length_type != null) { field.set_attribute_string ("CCode", "array_length_type", array_length_type); } } else if (field.variable_type is ArrayType) { field.set_attribute_bool ("CCode", "array_length", false); } if (no_delegate_target) { field.set_attribute_bool ("CCode", "delegate_target", false); } return field; } private string[]? get_attributes (string codenode) { var attributes = codenode_attributes_map.get (codenode); if (attributes == null) { var dot_required = (-1 != codenode.index_of_char ('.')); var colon_required = (-1 != codenode.index_of_char (':')); var pattern_specs = codenode_attributes_patterns.get_keys (); foreach (PatternSpec* pattern in pattern_specs) { var pspec = codenode_attributes_patterns[pattern]; if ((dot_required && -1 == pspec.index_of_char ('.')) || (colon_required && -1 == pspec.index_of_char (':'))) { continue; } if (pattern->match_string (codenode)) { return get_attributes (pspec); } } } if (attributes == null) { return null; } GLib.SList attr_list = new GLib.SList (); var attr = new GLib.StringBuilder.sized (attributes.length); var attributes_len = attributes.length; unowned string remaining = attributes; bool quoted = false, escaped = false; for (int b = 0 ; b < attributes_len ; b++) { unichar c = remaining.get_char (); if (escaped) { escaped = false; attr.append_unichar (c); } else { if (c == '"') { attr.append_unichar (c); quoted = !quoted; } else if (c == '\\') { escaped = true; } else if (!quoted && (c == ' ')) { attr_list.prepend (attr.str); attr.truncate (0); } else { attr.append_unichar (c); } } remaining = (string) ((char*) remaining + remaining.index_of_nth_char (1)); } if (attr.len > 0) { attr_list.prepend (attr.str); } var attrs = new string[attr_list.length ()]; unowned GLib.SList? attr_i = attr_list; for (int a = 0 ; a < attrs.length ; a++, attr_i = attr_i.next) { attrs[(attrs.length - 1) - a] = attr_i.data; } return attrs; } private string eval (string s) { return ((s.length >= 2) && s.has_prefix ("\"") && s.has_suffix ("\"")) ? s.substring (1, s.length - 2) : s; } private Signal? parse_signal (IdlNodeSignal sig_node) { weak IdlNode node = (IdlNode) sig_node; if (sig_node.deprecated || sig_node.result == null) { return null; } var sig = new Signal (fix_prop_name (node.name), parse_param (sig_node.result), current_source_reference); sig.access = SymbolAccessibility.PUBLIC; var attributes = get_attributes ("%s::%s".printf (get_cname (current_data_type), sig.name)); if (attributes != null) { string ns_name = null; foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "name") { sig.set_attribute_string ("CCode", "cname", sig.name.replace ("_", "-")); sig.name = eval (nv[1]); } else if (nv[0] == "has_emitter" && eval (nv[1]) == "1") { sig.set_attribute ("HasEmitter", true); } else if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { return null; } } else if (nv[0] == "deprecated") { if (eval (nv[1]) == "1") { sig.set_attribute_bool ("Version", "deprecated", true); } } else if (nv[0] == "replacement") { sig.set_attribute_string ("Version", "replacement", eval (nv[1])); } else if (nv[0] == "deprecated_since") { sig.set_attribute_string ("Version", "deprecated_since", eval (nv[1])); } else if (nv[0] == "transfer_ownership") { if (eval (nv[1]) == "1") { sig.return_type.value_owned = true; } } else if (nv[0] == "namespace_name") { ns_name = eval (nv[1]); } else if (nv[0] == "type_name") { sig.return_type = parse_type_from_string (eval (nv[1]), false); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (sig.return_type, eval (nv[1])); } else if (nv[0] == "experimental") { if (eval (nv[1]) == "1") { sig.set_attribute_bool ("Version", "experimental", true); } } } if (ns_name != null) { ((UnresolvedType) sig.return_type).unresolved_symbol.inner = new UnresolvedSymbol (null, ns_name); } } sig.is_virtual = true; bool first = true; foreach (weak IdlNodeParam param in sig_node.parameters) { if (first) { // ignore implicit first signal parameter (sender) first = false; continue; } weak IdlNode param_node = (IdlNode) param; ParameterDirection direction; var param_type = parse_param (param, out direction); var p = new Parameter (param_node.name, param_type, current_source_reference); p.direction = direction; bool hide_param = false; bool show_param = false; attributes = get_attributes ("%s::%s.%s".printf (get_cname (current_data_type), sig.name, param_node.name)); if (attributes != null) { string ns_name = null; foreach (string attr in attributes) { var nv = attr.split ("=", 2); if (nv[0] == "hidden") { if (eval (nv[1]) == "1") { hide_param = true; } else if (eval (nv[1]) == "0") { show_param = true; } } else if (nv[0] == "is_array") { if (eval (nv[1]) == "1") { param_type.value_owned = true; param_type = new ArrayType (param_type, 1, param_type.source_reference); p.variable_type = param_type; p.direction = ParameterDirection.IN; } } else if (nv[0] == "no_array_length") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); } } else if (nv[0] == "array_length_type") { p.set_attribute_string ("CCode", "array_length_type", nv[1]); } else if (nv[0] == "array_null_terminated") { if (eval (nv[1]) == "1") { p.set_attribute_bool ("CCode", "array_length", false); p.set_attribute_bool ("CCode", "array_null_terminated", true); } } else if (nv[0] == "is_out") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.OUT; } } else if (nv[0] == "is_ref") { if (eval (nv[1]) == "1") { p.direction = ParameterDirection.REF; } } else if (nv[0] == "nullable") { if (eval (nv[1]) == "1" && !(param_type is VoidType)) { param_type.nullable = true; } } else if (nv[0] == "transfer_ownership") { if (eval (nv[1]) == "1") { param_type.value_owned = true; } } else if (nv[0] == "type_name") { p.variable_type = param_type = parse_type_from_string (eval (nv[1]), false); } else if (nv[0] == "type_arguments") { parse_type_arguments_from_string (p.variable_type, eval (nv[1])); } else if (nv[0] == "namespace_name") { ns_name = eval (nv[1]); } } if (ns_name != null) { ((UnresolvedType) param_type).unresolved_symbol.inner = new UnresolvedSymbol (null, ns_name); } } if (show_param || !hide_param) { sig.add_parameter (p); } } return sig; } } // vim:sw=8 noet