Add pyflakes.py and run it in make check. Update the source code to fix
[platform/upstream/gobject-introspection.git] / giscanner / glibtransformer.py
index 3b24db6..564e2d9 100644 (file)
 #
 
 import ctypes
-import re
-import os
 
 from . import cgobject
 from .odict import odict
-from .ast import (Callback, Enum, Function, Parameter, Property, Return,
-                  Sequence, Struct)
+from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
+                  Property, Return, Struct, Type, Alias, type_name_from_ctype)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal)
-
-
-# Copied from h2defs.py
-_upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
-_upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
-_upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
-
-def to_underscores(name):
-    """Converts a typename to the equivalent underscores name.
-    This is used to form the type conversion macros and enum/flag
-    name variables"""
-    name = _upperstr_pat1.sub(r'\1_\2', name)
-    name = _upperstr_pat2.sub(r'\1_\2', name)
-    name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
-    return name
-
-_libtool_pat = re.compile("dlname='([A-z0-9\.]+)'\n")
-
-def resolve_libtool(libname):
-    data = open(libname).read()
-    filename = _libtool_pat.search(data).groups()[0]
-    libname = os.path.join(os.path.dirname(libname),
-                           '.libs', filename)
-    return libname
-
-
+from .utils import extract_libtool, to_underscores
 
 
 class GLibTransformer(object):
-    def __init__(self, namespace_name):
-        self._namespace_name = namespace_name
+
+    def __init__(self, transformer):
+        self._transformer = transformer
+        self._namespace_name = None
+        self._aliases = []
         self._output_ns = odict()
-        self._library = None
-        self._type_names = {}
+        self._libraries = []
+        self._internal_types = {}
 
     # Public API
 
-    def get_nodes(self):
-        return self._output_ns.values()
-
-    def load_library(self, libname):
+    def add_library(self, libname):
         if libname.endswith('.la'):
-            libname = resolve_libtool(libname)
-        self._library = ctypes.cdll.LoadLibrary(libname)
+            libname = extract_libtool(libname)
+        self._libraries.append(ctypes.cdll.LoadLibrary(libname))
 
-    def parse(self, nodes):
-        for node in nodes:
+    def parse(self):
+        namespace = self._transformer.parse()
+        self._namespace_name = namespace.name
+
+        # First pass, parsing
+        for node in namespace.nodes:
             self._parse_node(node)
 
-        # Second round, associate GtkButtonClass with GtkButton
+        # Introspection is done from within parsing
+
+        # Second pass, resolving types
         for node in self._output_ns.values():
+            # associate GtkButtonClass with GtkButton
             if isinstance(node, Struct):
                 self._pair_class_struct(node)
+            self._resolve_node(node)
 
-    def register_include(self, filename):
-        if filename.endswith('.gir'):
-            from .girparser import GIRParser
-            parser = GIRParser(filename)
-        elif filename.endswith('.gidl'):
-            from .gidlparser import GIDLParser
-            parser = GIDLParser(filename)
-        else:
-            raise NotImplementedError(filename)
-        nsname = parser.get_namespace_name()
-        for node in parser.get_nodes():
-            self._type_names[node.type_name] = (nsname, node)
+        # Create a new namespace with what we found
+        namespace = Namespace(namespace.name)
+        namespace.nodes = self._aliases + self._output_ns.values()
+        return namespace
 
     # Private
 
@@ -110,37 +83,33 @@ class GLibTransformer(object):
         return self._output_ns.get(name)
 
     def _register_internal_type(self, type_name, node):
-        self._type_names[type_name] = (None, node)
-
-    def _strip_namespace_func(self, name):
-        orig_name = name
-        prefix = self._namespace_name.lower() + '_'
-        name = name.lower()
-        if name.startswith(prefix):
-            name = orig_name[len(prefix):]
-        return name
-
-    def _strip_namespace_object(self, name):
-        orig_name = name
-        prefix = self._namespace_name.lower()
-        name = name.lower()
-        if name.startswith(prefix):
-            name = orig_name[len(prefix):]
-        return name
+        self._internal_types[type_name] = node
 
-    def _resolve_type_name(self, type_name):
-        item = self._type_names.get(type_name)
-        if item is not None:
-            nsname, item = item
-            if nsname is None:
-                return item.name
-            return '%s.%s' % (nsname, item.name)
-        return type_name
+    # Helper functions
 
-    def _resolve_param_type(self, ptype):
-        type_name = ptype.name.replace('*', '')
-        ptype.name = self._resolve_type_name(type_name)
-        return ptype
+    def _create_type(self, type_id):
+        ctype = cgobject.type_name(type_id)
+        type_name = type_name_from_ctype(ctype)
+        return Type(type_name, ctype)
+
+    def _create_gobject(self, node):
+        type_name = 'G' + node.name
+        if type_name == 'GObject':
+            parent_type_name = None
+            symbol = 'intern'
+        else:
+            type_id = cgobject.type_from_name(type_name)
+            parent_type_name = cgobject.type_name(
+                cgobject.type_parent(type_id))
+            symbol = to_underscores(type_name).lower() + '_get_type'
+        node = GLibObject(node.name, parent_type_name, type_name, symbol)
+        type_id = cgobject.TYPE_OBJECT
+        self._introspect_properties(node, type_id)
+        self._introspect_signals(node, type_id)
+        self._add_attribute(node)
+        self._register_internal_type(type_name, node)
+
+    # Parser
 
     def _parse_node(self, node):
         if isinstance(node, Enum):
@@ -151,11 +120,18 @@ class GLibTransformer(object):
             self._parse_struct(node)
         elif isinstance(node, Callback):
             self._parse_callback(node)
+        elif isinstance(node, Alias):
+            self._parse_alias(node)
+        elif isinstance(node, Member):
+            # FIXME: atk_misc_instance singletons
+            pass
         else:
             print 'GOBJECT BUILDER: Unhandled node:', node
 
+    def _parse_alias(self, alias):
+        self._aliases.append(alias)
+
     def _parse_enum(self, enum):
-        enum.name = self._strip_namespace_object(enum.name)
         self._add_attribute(enum)
 
     def _parse_function(self, func):
@@ -166,27 +142,30 @@ class GLibTransformer(object):
         elif self._parse_method(func):
             return
 
-        self._parse_parameters(func.parameters)
-        func.retval.type = self._resolve_param_type(func.retval.type)
-
-        func.name = self._strip_namespace_func(func.name)
         self._add_attribute(func)
 
-    def _parse_parameters(self, parameters):
-        for parameter in parameters:
-            parameter.type = self._resolve_param_type(parameter.type)
-
     def _parse_get_type_function(self, func):
+        if not self._libraries:
+            return False
         # GType *_get_type(void)
         symbol = func.symbol
         if not symbol.endswith('_get_type'):
             return False
-        if func.retval.type.name != 'GType':
+        if func.retval.type.name != 'GObject.GType':
             return False
         if func.parameters:
             return False
 
-        func = getattr(self._library, symbol)
+        for library in self._libraries:
+            try:
+                func = getattr(library, symbol)
+                break
+            except AttributeError:
+                continue
+        else:
+            print 'Warning: could not find symbol: %s' % symbol
+            return False
+
         func.restype = cgobject.GType
         func.argtypes = []
         type_id = func()
@@ -228,21 +207,28 @@ class GLibTransformer(object):
 
         # GtkButton -> gtk_button_, so we can figure out the method name
         prefix = to_underscores(orig_name).lower() + '_'
-        if not func.name.startswith(prefix):
+        if not func.symbol.startswith(prefix):
             return False
 
         # Strip namespace and object prefix: gtk_window_new -> new
-        func.name = func.name[len(prefix):]
+        func.name = func.symbol[len(prefix):]
         if is_method:
             class_.methods.append(func)
         else:
             class_.constructors.append(func)
-        self._parse_parameters(func.parameters)
-        func.retval.type = self._resolve_param_type(func.retval.type)
         return True
 
     def _parse_struct(self, struct):
-        self._add_attribute(struct)
+        # This is a hack, but GObject is a rather fundamental piece so.
+        if (self._namespace_name == 'GObject' and
+            struct.name in ["Object", 'InitiallyUnowned']):
+            self._create_gobject(struct)
+            return
+        node = self._output_ns.get(struct.name)
+        if node is None:
+            self._add_attribute(struct, replace=True)
+            return
+        node.fields = struct.fields[:]
 
     def _parse_callback(self, callback):
         self._add_attribute(callback)
@@ -259,9 +245,13 @@ class GLibTransformer(object):
 
         node = self._output_ns.get(self._resolve_type_name(name))
         del self._output_ns[class_node.name]
+        if node is None:
+            return
         for field in class_node.fields[1:]:
             node.fields.append(field)
 
+    # Introspection
+
     def _introspect_type(self, type_id, symbol):
         fundamental_type_id = cgobject.type_fundamental(type_id)
         if (fundamental_type_id == cgobject.TYPE_ENUM or
@@ -274,7 +264,7 @@ class GLibTransformer(object):
         elif fundamental_type_id == cgobject.TYPE_BOXED:
             self._introspect_boxed(type_id, symbol)
         else:
-            print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
+            print 'unhandled GType: %s' % (cgobject.type_name(type_id), )
 
     def _introspect_enum(self, ftype_id, type_id, symbol):
         type_class = cgobject.type_class_ref(type_id)
@@ -283,33 +273,42 @@ class GLibTransformer(object):
 
         members = []
         for enum_value in type_class.get_values():
-            members.append(GLibEnumMember(enum_value.value_name,
+            members.append(GLibEnumMember(enum_value.value_nick,
                                           enum_value.value,
+                                          enum_value.value_name,
                                           enum_value.value_nick))
 
         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
         type_name = cgobject.type_name(type_id)
-        node = klass(self._strip_namespace_object(type_name),
-                      members, type_name, symbol)
+        enum_name = self._transformer.strip_namespace_object(type_name)
+        node = klass(enum_name, type_name, members, symbol)
         self._add_attribute(node, replace=True)
         self._register_internal_type(type_name, node)
 
     def _introspect_object(self, type_id, symbol):
         type_name = cgobject.type_name(type_id)
         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
-        node = GLibObject(self._strip_namespace_object(type_name),
-                          self._resolve_type_name(parent_type_name),
-                          type_name, symbol)
+        node = GLibObject(
+            self._transformer.strip_namespace_object(type_name),
+            self._resolve_type_name(parent_type_name),
+            type_name, symbol)
         self._introspect_properties(node, type_id)
         self._introspect_signals(node, type_id)
         self._add_attribute(node)
-        self._remove_attribute(type_name)
+        try:
+            self._remove_attribute(type_name)
+        except KeyError:
+            print 'Warning: could not remove %s' % type_name
+            pass
         self._register_internal_type(type_name, node)
 
     def _introspect_interface(self, type_id, symbol):
         type_name = cgobject.type_name(type_id)
-        node = GLibInterface(self._strip_namespace_object(type_name),
-                             type_name, symbol)
+        parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
+        node = GLibInterface(
+            self._transformer.strip_namespace_object(type_name),
+            self._resolve_type_name(parent_type_name),
+            type_name, symbol)
         self._introspect_properties(node, type_id)
         self._introspect_signals(node, type_id)
         self._add_attribute(node)
@@ -317,10 +316,14 @@ class GLibTransformer(object):
 
     def _introspect_boxed(self, type_id, symbol):
         type_name = cgobject.type_name(type_id)
-        node = GLibBoxed(self._strip_namespace_object(type_name),
+        node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
                          type_name, symbol)
         self._add_attribute(node)
-        self._remove_attribute(type_name)
+        # GdkEvent raises KeyError, FooBoxed ends up duplicated if we don't
+        try:
+            self._remove_attribute(type_name)
+        except KeyError:
+            pass
         self._register_internal_type(type_name, node)
 
     def _introspect_properties(self, node, type_id):
@@ -335,19 +338,111 @@ class GLibTransformer(object):
         for pspec in pspecs:
             if pspec.owner_type != type_id:
                 continue
+            ctype = cgobject.type_name(pspec.value_type)
             node.properties.append(Property(
                 pspec.name,
-                cgobject.type_name(pspec.value_type)))
+                type_name_from_ctype(ctype),
+                ctype,
+                ))
 
     def _introspect_signals(self, node, type_id):
         for signal_info in cgobject.signal_list(type_id):
-            return_ = Return(cgobject.type_name(signal_info.return_type))
+            rtype = self._create_type(signal_info.return_type)
+            return_ = Return(rtype)
             signal = GLibSignal(signal_info.signal_name, return_)
             for i, parameter in enumerate(signal_info.get_params()):
                 if i == 0:
                     name = 'object'
                 else:
-                    name = 'p%s' % (i-1,)
-                signal.parameters.append(
-                    Parameter(name, cgobject.type_name(parameter)))
+                    name = 'p%s' % (i-1, )
+                ptype = self._create_type(parameter)
+                param = Parameter(name, ptype)
+                signal.parameters.append(param)
             node.signals.append(signal)
+
+    # Resolver
+
+    def _resolve_type_name(self, type_name):
+        type_name = type_name.replace('*', '')
+        possible_name = self._transformer.resolve_type_name(type_name)
+        if possible_name != type_name:
+            return possible_name
+        possible_node = self._internal_types.get(type_name)
+        if possible_node:
+            return possible_node.name
+        return type_name
+
+    def _resolve_param_type(self, ptype):
+        ptype.name = ptype.name.replace('*', '')
+        type_name = ptype.name
+        type_name = self._transformer.resolve_possible_typedef(type_name)
+        possible_node = self._internal_types.get(type_name)
+        if possible_node:
+            ptype.name = possible_node.name
+        else:
+            ptype = self._transformer.resolve_param_type(ptype)
+        return ptype
+
+    def _resolve_node(self, node):
+        if isinstance(node, (Callback, Function)):
+            self._resolve_function(node)
+        elif isinstance(node, GLibObject):
+            self._resolve_glib_object(node)
+        elif isinstance(node, GLibInterface):
+            self._resolve_glib_interface(node)
+        elif isinstance(node, GLibBoxed):
+            self._resolve_glib_boxed(node)
+        elif isinstance(node, Struct):
+            self._resolve_struct(node)
+
+    def _resolve_struct(self, node):
+        for field in node.fields:
+            self._resolve_field(field)
+
+    def _resolve_glib_interface(self, node):
+        self._resolve_methods(node.methods)
+        self._resolve_properties(node.properties)
+        self._resolve_signals(node.signals)
+
+    def _resolve_glib_object(self, node):
+        self._resolve_constructors(node.constructors)
+        self._resolve_methods(node.methods)
+        self._resolve_properties(node.properties)
+        self._resolve_signals(node.signals)
+
+    def _resolve_glib_boxed(self, node):
+        self._resolve_constructors(node.constructors)
+        self._resolve_methods(node.methods)
+
+    def _resolve_constructors(self, constructors):
+        for ctor in constructors:
+            self._resolve_function(ctor)
+
+    def _resolve_methods(self, methods):
+        for method in methods:
+            self._resolve_function(method)
+
+    def _resolve_signals(self, signals):
+        for signal in signals:
+            self._resolve_function(signal)
+
+    def _resolve_properties(self, properties):
+        for prop in properties:
+            self._resolve_property(prop)
+
+    def _resolve_property(self, prop):
+        prop.type = self._resolve_param_type(prop.type)
+
+    def _resolve_function(self, func):
+        self._resolve_parameters(func.parameters)
+        func.retval.type = self._resolve_param_type(func.retval.type)
+
+    def _resolve_parameters(self, parameters):
+        for parameter in parameters:
+            parameter.type = self._resolve_param_type(parameter.type)
+
+    def _resolve_field(self, field):
+        if isinstance(field, Callback):
+            self._resolve_function(field)
+            return
+        field.type = self._resolve_param_type(field.type)