# HG changeset patch
# User Dag Sverre Seljebotn <dagss@student.matnat.uio.no>
# Date 1265206417 -3600
#      Wed Feb 03 15:13:37 2010 +0100
# Node ID 044b7fc4c386f9cd95b56b448319420236f063cd
# Parent  c1f075d3f3ff39737ff839fa048debe3e52151dd
A gentle start on #469

diff --git a/Cython/Compiler/Annotate.py b/Cython/Compiler/Annotate.py
--- a/Cython/Compiler/Annotate.py
+++ b/Cython/Compiler/Annotate.py
@@ -19,8 +19,8 @@
 
 class AnnotationCCodeWriter(CCodeWriter):
 
-    def __init__(self, create_from=None, buffer=None, copy_formatting=True):
-        CCodeWriter.__init__(self, create_from, buffer, copy_formatting=True)
+    def __init__(self, create_from=None, buffer=None, copy_formatting=True, description=None):
+        CCodeWriter.__init__(self, create_from, buffer, copy_formatting=True, description=description)
         if create_from is None:
             self.annotation_buffer = StringIO()
             self.annotations = []
@@ -33,8 +33,8 @@
             self.code = create_from.code
             self.last_pos = create_from.last_pos
     
-    def create_new(self, create_from, buffer, copy_formatting):
-        return AnnotationCCodeWriter(create_from, buffer, copy_formatting)
+    def create_new(self, create_from, buffer, copy_formatting, description=None):
+        return AnnotationCCodeWriter(create_from, buffer, copy_formatting, description=description)
 
     def write(self, s):
         CCodeWriter.write(self, s)
diff --git a/Cython/Compiler/Buffer.py b/Cython/Compiler/Buffer.py
--- a/Cython/Compiler/Buffer.py
+++ b/Cython/Compiler/Buffer.py
@@ -445,8 +445,8 @@
             params.append(s)
         
     # Make sure the utility code is available
-    if funcname not in code.globalstate.utility_codes:
-        code.globalstate.utility_codes.add(funcname)
+    if funcname not in code.globalstate.processed_objects:
+        code.globalstate.processed_objects.add(funcname)
         protocode = code.globalstate['utility_code_proto']
         defcode = code.globalstate['utility_code_def']
         funcgen(protocode, defcode, name=funcname, nd=nd)
@@ -665,8 +665,8 @@
     if maxdepth <= 0:
         assert False
 
-    if name not in code.globalstate.utility_codes:
-        code.globalstate.utility_codes.add(name)
+    if name not in code.globalstate.processed_objects:
+        code.globalstate.processed_objects.add(name)
         typecode = code.globalstate['typeinfo']
         
         complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -21,8 +21,6 @@
     # Stores utility code to add during code generation.
     #
     # See GlobalState.put_utility_code.
-    #
-    # hashes/equals by instance
 
     def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None,
                  proto_block='utility_code_proto'):
@@ -62,9 +60,6 @@
             return s
 
     def put_code(self, output):
-        if self.requires:
-            for dependency in self.requires:
-                output.use_utility_code(dependency)
         if self.proto:
             output[self.proto_block].put(self.proto)
         if self.impl:
@@ -384,8 +379,6 @@
     #                                  to create this output C code.  This is
     #                                  used to annotate the comments.
     #
-    # utility_codes   set                IDs of used utility code (to avoid reinsertion)
-    #
     # declared_cnames  {string:Entry}  used in a transition phase to merge pxd-declared
     #                                  constants etc. into the pyx-declared ones (i.e,
     #                                  check if constants are already added).
@@ -394,6 +387,8 @@
     #
     # const_cname_counter int          global counter for constant identifiers
     #
+    # processed_objects  set()         objects which has had their C code generated
+    # process_object_stack [object]    (internal use)
 
     # parts            {string:CCodeWriter}
 
@@ -434,11 +429,13 @@
     ]
     
 
-    def __init__(self, writer, module_node, emit_linenums=False):
+    def __init__(self, writer, module_node, emit_linenums=False,
+                 header_mode=False):
         self.filename_table = {}
         self.filename_list = []
         self.input_file_contents = {}
-        self.utility_codes = set()
+        self.process_objects_stack = []
+        self.processed_objects = set()
         self.declared_cnames = {}
         self.in_utility_code_generation = False
         self.emit_linenums = emit_linenums
@@ -454,11 +451,14 @@
         assert writer.globalstate is None
         writer.globalstate = self
         self.rootwriter = writer
+        self.header_mode = header_mode
+        if header_mode:
+            self.parts = {'header' : writer }
 
     def initialize_main_c_code(self):
         rootwriter = self.rootwriter
         for part in self.code_layout:
-            self.parts[part] = rootwriter.insertion_point()
+            self.parts[part] = rootwriter.insertion_point(part)
 
         if not Options.cache_builtins:
             del self.parts['cached_builtins']
@@ -777,21 +777,38 @@
             return F
 
     #
-    # Utility code state
+    # Stuff about asking objects to output themselves to C
+    # Utility code handled here.
     #
+    def put_object(self, obj):
+        """
+        Write the C code associated with the given object (such as a type definition,
+        some utility code, etc.). The object:
+         - Must have a "requires" attribute (list of other objects which
+           must be output first)
+         - Must have an __eq__/__hash__, used to determine whether the same
+           object has already been output
+         - Must have a method put_code(self, output), which is called to output
+           the object to C. Output should be indexed with the required part, e.g.
+           writer = output['utility_code_proto']
+
+        There's no guarantee on when put_code will be called, but currently it
+        happens right away (but only once per object).
+        """
+        if obj in self.processed_objects:
+            return
+        if obj in self.process_objects_stack:
+            raise AssertionError("Object dependency graph is not a DAG, found a loop")
+        self.process_objects_stack.append(obj)
+        if obj.requires is not None and obj.requires:
+            for req in obj.requires:
+                self.put_object(req)
+        self.process_objects_stack.pop()
+        self.processed_objects.add(obj)
+        obj.put_code(self)
     
     def use_utility_code(self, utility_code):
-        """
-        Adds code to the C file. utility_code should
-        a) implement __eq__/__hash__ for the purpose of knowing whether the same
-           code has already been included
-        b) implement put_code, which takes a globalstate instance
-
-        See UtilityCode.
-        """
-        if utility_code not in self.utility_codes:
-            self.utility_codes.add(utility_code)
-            utility_code.put_code(self)
+        self.put_object(utility_code)
 
 
 def funccontext_property(name):
@@ -843,7 +860,11 @@
 
     globalstate = None
     
-    def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
+    def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None,
+                 description=None):
+        if description is None:
+            description = '<not specified>'
+        self.description = description
         if buffer is None: buffer = StringIOTree()
         self.buffer = buffer
         self.marker = None
@@ -869,10 +890,13 @@
         else:
             self.emit_linenums = emit_linenums
 
-    def create_new(self, create_from, buffer, copy_formatting):
+    def __repr__(self):
+        return '%s@%s' % (object.__repr__(self), self.description)
+
+    def create_new(self, create_from, buffer, copy_formatting, description='(some create_new)'):
         # polymorphic constructor -- very slightly more versatile
         # than using __class__
-        result = CCodeWriter(create_from, buffer, copy_formatting)
+        result = CCodeWriter(create_from, buffer, copy_formatting, description=description)
         return result
 
     def copyto(self, f):
@@ -884,8 +908,9 @@
     def write(self, s):
         self.buffer.write(s)
 
-    def insertion_point(self):
-        other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True)
+    def insertion_point(self, description='(some insertion point)'):
+        other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(),
+                                copy_formatting=True, description=description)
         return other
 
     def new_writer(self):
@@ -1331,3 +1356,15 @@
     def dedent(self):
         self.level -= 1
 
+
+#
+# C writing utility functions
+#
+def sue_header_footer(type, kind, name):
+    if type.typedef_flag:
+        header = "typedef %s {" % kind
+        footer = "} %s;" % name
+    else:
+        header = "%s %s {" % (kind, name)
+        footer = "};"
+    return header, footer        
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -124,7 +124,7 @@
         if h_types or h_vars or h_funcs or h_extension_types:
             result.h_file = replace_suffix(result.c_file, ".h")
             h_code = Code.CCodeWriter()
-            Code.GlobalState(h_code, self)
+            Code.GlobalState(h_code, self, header_mode=True)
             if options.generate_pxi:
                 result.i_file = replace_suffix(result.c_file, ".pxi")
                 i_code = Code.PyrexCodeWriter(result.i_file)
@@ -408,10 +408,8 @@
                         self.generate_struct_union_definition(entry, code)
                     elif type.is_enum:
                         self.generate_enum_definition(entry, code)
-                    elif type.is_extension_type and entry not in vtabslot_entries:
-                        self.generate_obj_struct_definition(type, code)
-        for entry in vtabslot_list:
-            self.generate_obj_struct_definition(entry.type, code)
+                    elif type.is_extension_type:# and entry not in vtabslot_entries:
+                        code.globalstate.put_object(type)
         for entry in vtab_list:
             self.generate_typeobject_predeclaration(entry, code)
             self.generate_exttype_vtable_struct(entry, code)
@@ -660,7 +658,6 @@
         #for entry in env.type_entries:
         for entry in type_entries:
             if not entry.in_cinclude:
-                #print "generate_type_header_code:", entry.name, repr(entry.type) ###
                 type = entry.type
                 if type.is_typedef: # Must test this first!
                     self.generate_typedef(entry, code)
@@ -669,7 +666,7 @@
                 elif type.is_enum:
                     self.generate_enum_definition(entry, code)
                 elif type.is_extension_type:
-                    self.generate_obj_struct_definition(type, code)
+                    code.globalstate.put_object(type)
         
     def generate_gcc33_hack(self, env, code):
         # Workaround for spurious warning generation in gcc 3.3
@@ -694,15 +691,6 @@
         writer.putln("")
         writer.putln("typedef %s;" % base_type.declaration_code(entry.cname))
 
-    def sue_header_footer(self, type, kind, name):
-        if type.typedef_flag:
-            header = "typedef %s {" % kind
-            footer = "} %s;" % name
-        else:
-            header = "%s %s {" % (kind, name)
-            footer = "};"
-        return header, footer
-    
     def generate_struct_union_definition(self, entry, code):
         code.mark_pos(entry.pos)
         type = entry.type
@@ -714,7 +702,7 @@
                 kind = "%s %s" % (type.kind, "__Pyx_PACKED")
                 code.globalstate.use_utility_code(packed_struct_utility_code)
             header, footer = \
-                self.sue_header_footer(type, kind, type.cname)
+                Code.sue_header_footer(type, kind, type.cname)
             code.putln("")
             if packed:
                 code.putln("#if !defined(__GNUC__)")
@@ -741,7 +729,7 @@
         type = entry.type
         name = entry.cname or entry.name or ""
         header, footer = \
-            self.sue_header_footer(type, "enum", name)
+            Code.sue_header_footer(type, "enum", name)
         code.putln("")
         code.putln(header)
         enum_values = entry.enum_values
@@ -814,40 +802,6 @@
             code.putln("static struct %s *%s;" % (
                 type.vtabstruct_cname,
                 type.vtabptr_cname))
-    
-    def generate_obj_struct_definition(self, type, code):
-        code.mark_pos(type.pos)
-        # Generate object struct definition for an
-        # extension type.
-        if not type.scope:
-            return # Forward declared but never defined
-        header, footer = \
-            self.sue_header_footer(type, "struct", type.objstruct_cname)
-        code.putln("")
-        code.putln(header)
-        base_type = type.base_type
-        if base_type:
-            code.putln(
-                "%s%s %s;" % (
-                    ("struct ", "")[base_type.typedef_flag],
-                    base_type.objstruct_cname,
-                    Naming.obj_base_cname))
-        else:
-            code.putln(
-                "PyObject_HEAD")
-        if type.vtabslot_cname and not (type.base_type and type.base_type.vtabslot_cname):
-            code.putln(
-                "struct %s *%s;" % (
-                    type.vtabstruct_cname,
-                    type.vtabslot_cname))
-        for attr in type.scope.var_entries:
-            code.putln(
-                "%s;" %
-                    attr.type.declaration_code(attr.cname))
-        code.putln(footer)
-        if type.objtypedef_cname is not None:
-            # Only for exposing public typedef name.
-            code.putln("typedef struct %s %s;" % (type.objstruct_cname, type.objtypedef_cname))
 
     def generate_global_declarations(self, env, code, definition):
         code.putln("")
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -106,6 +106,7 @@
     has_attributes = 0
     default_value = ""
     pymemberdef_typecode = None
+    requires = None
     
     def resolve(self):
         # If a typedef, returns the base type.
@@ -645,6 +646,7 @@
     #  vtabstruct_cname string           Name of C method table struct
     #  vtabptr_cname    string           Name of pointer to C method table
     #  vtable_cname     string           Name of C method table definition
+    #  in_cinclude      boolean          Corresponds to the in_cinclude of the corresponding entry
     
     is_extension_type = 1
     has_attributes = 1
@@ -670,6 +672,10 @@
         self.vtabptr_cname = None
         self.vtable_cname = None
         self.is_external = is_external
+        if base_type:
+            self.requires = [base_type]
+        else:
+            self.requires = ()
     
     def set_scope(self, scope):
         self.scope = scope
@@ -727,6 +733,50 @@
         return "<PyExtensionType %s%s>" % (self.scope.class_name,
             ("", " typedef")[self.typedef_flag])
     
+    
+    def generate_obj_struct_definition(self, code):
+        import Code
+        code.mark_pos(self.pos)
+        # Generate object struct definition for an
+        # extension type.
+        if not self.scope:
+            return # Forward declared but never defined
+        header, footer = \
+            Code.sue_header_footer(self, "struct", self.objstruct_cname)
+        code.putln("")
+        code.putln(header)
+        base_type = self.base_type
+        if base_type:
+            code.putln(
+                "%s%s %s;" % (
+                    ("struct ", "")[base_type.typedef_flag],
+                    base_type.objstruct_cname,
+                    Naming.obj_base_cname))
+        else:
+            code.putln(
+                "PyObject_HEAD")
+        if self.vtabslot_cname and not (self.base_type and self.base_type.vtabslot_cname):
+            code.putln(
+                "struct %s *%s;" % (
+                    self.vtabstruct_cname,
+                    self.vtabslot_cname))
+        for attr in self.scope.var_entries:
+            code.putln(
+                "%s;" %
+                    attr.type.declaration_code(attr.cname))
+        code.putln(footer)
+        if self.objtypedef_cname is not None:
+            # Only for exposing public typedef name.
+            code.putln("typedef struct %s %s;" % (self.objstruct_cname, self.objtypedef_cname))
+
+    def put_code(self, output):
+        if not self.in_cinclude:
+            if output.header_mode:
+                code = output['header']
+            else:
+                code = output['type_declarations']
+            self.generate_obj_struct_definition(code)
+
 
 class CType(PyrexType):
     #
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -905,6 +905,7 @@
         #
         if not entry:
             type = PyrexTypes.PyExtensionType(name, typedef_flag, base_type, visibility == 'extern')
+            # type.in_cinclude is set further down
             type.pos = pos
             type.buffer_defaults = buffer_defaults
             if objtypedef_cname is not None:
@@ -920,6 +921,7 @@
             entry = self.declare_type(name, type, pos, visibility = visibility,
                 defining = 0)
             entry.is_cclass = True
+            type.in_cinclude = entry.in_cinclude
             if objstruct_cname:
                 type.objstruct_cname = objstruct_cname
             elif not entry.in_cinclude: