nitg: intro the common_ffi module and support for the C language
authorAlexis Laferrière <alexis.laf@xymus.net>
Thu, 28 Nov 2013 19:19:04 +0000 (14:19 -0500)
committerJean Privat <jean@pryen.org>
Mon, 17 Feb 2014 19:08:38 +0000 (14:08 -0500)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

src/c_tools.nit
src/common_ffi/c.nit [new file with mode: 0644]
src/common_ffi/common_ffi.nit [new file with mode: 0644]
src/common_ffi/extern_classes.nit [new file with mode: 0644]
src/common_ffi/ffi_base.nit [new file with mode: 0644]
src/common_ffi/header_dependency.nit [new file with mode: 0644]

index 663a059..e05f4bf 100644 (file)
@@ -45,7 +45,7 @@ class CCompilationUnit
        var body_impl : Writer = new Writer
 
        # files to compile TODO check is appropriate
-       #var files = new Array[String]
+       var files = new Array[String]
 
        fun add_local_function( efc : CFunction )
        do
diff --git a/src/common_ffi/c.nit b/src/common_ffi/c.nit
new file mode 100644 (file)
index 0000000..661928c
--- /dev/null
@@ -0,0 +1,167 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Support for nesting C code within a Nit program using its FFI
+module c
+
+import ffi_base
+
+redef class FFILanguageAssignationPhase
+       var c_language: FFILanguage = new CLanguage(self)
+end
+
+class CLanguage
+       super FFILanguage
+
+       redef fun identify_language(n) do return n.is_c
+
+       redef fun compile_module_block(block, ecc, nmodule)
+       do
+               if block.is_c_header then
+                       ecc.header_custom.add( block.location.as_line_pragma )
+                       ecc.header_custom.add( block.code )
+               else if block.is_c_body then
+                       ecc.body_custom.add( block.location.as_line_pragma )
+                       ecc.body_impl.add( block.code )
+               end
+       end
+
+       redef fun compile_extern_method(block, m, ecc, nmodule)
+       do
+               var fc = new ExternCFunction(m, nmodule.mmodule.as(not null))
+               fc.decls.add( block.location.as_line_pragma )
+               fc.exprs.add( block.code )
+               ecc.add_exported_function( fc )
+       end
+
+       redef fun compile_extern_class(block, m, ecc, nmodule) do end
+
+       redef fun get_ftype(block, m) do return new ForeignCType(block.code)
+
+       redef fun compile_callback(callback, nmodule, mmodule, ecc)
+       do
+               callback.compile_callback_to_c(mmodule, ecc)
+       end
+end
+
+redef class AExternCodeBlock
+       fun is_c: Bool do return language_name == null or
+               language_name_lowered == "c" or language_name_lowered.has_prefix( "c " )
+
+       fun is_c_body: Bool do return language_name == null or
+               language_name_lowered == "c" or language_name_lowered ==  "c body"
+
+       fun is_c_header: Bool do return language_name_lowered == "c header"
+end
+
+redef class Location
+       fun as_line_pragma: String do return "#line {line_start} \"{file.filename}\"\n"
+end
+
+redef class AModule
+       var c_compiler_options writable = ""
+       var c_linker_options writable = ""
+end
+
+# An extern C file to compile
+class ExternCFile
+       super ExternFile
+
+       init (filename, cflags: String)
+       do
+               super filename
+
+               self.cflags = cflags
+       end
+
+       # Additionnal specific CC compiler -c flags
+       var cflags: String
+
+       redef fun hash do return filename.hash
+       redef fun ==(o) do return o isa ExternCFile and filename == o.filename
+end
+
+class ForeignCType
+       super ForeignType
+
+       redef var ctype: String
+
+       init(ctype: String)
+       do
+               self.ctype = ctype
+       end
+end
+
+redef class NitniCallback
+       fun compile_callback_to_c(nmodule: MModule, ffi_ccu: CCompilationUnit) do end
+end
+
+redef class Object
+       # Context when calling user C code from generated code
+       fun to_c_call_context: ToCCallContext do return once new ToCCallContext
+
+       # Context when calling generated code from user C code
+       fun from_c_call_context: FromCCallContext do return once new FromCCallContext
+end
+
+redef class MExplicitCall
+       redef fun compile_callback_to_c(mmodule, ffi_ccu)
+       do
+               var mproperty = mproperty.as(MMethod)
+
+               var full_cname = mproperty.build_cname(recv_mtype, mmodule, null, long_signature)
+               var friendly_cname = mproperty.build_cname(recv_mtype, mmodule, null, short_signature)
+               ffi_ccu.body_decl.add("#define {friendly_cname} {full_cname}\n")
+       end
+end
+
+# Context when calling user C code from generated code
+class ToCCallContext
+       super CallContext
+
+       private init do end
+
+       redef fun name_mtype(mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind then return "void *"
+               return mtype.cname
+       end
+end
+
+# Context when calling generated code from user C code
+class FromCCallContext
+       super CallContext
+
+       private init do end
+
+       redef fun name_mtype(mtype) do return mtype.cname
+end
+
+class ExternCFunction
+       super CFunction
+
+       var method: AExternPropdef
+
+       init (method: AExternPropdef, mmodule: MModule)
+       do
+               self.method = method
+
+               var recv_mtype = method.mpropdef.mclassdef.bound_mtype
+               var csignature = method.mpropdef.mproperty.build_csignature(recv_mtype, mmodule, "___impl", long_signature, from_c_call_context)
+
+               super( csignature )
+       end
+end
diff --git a/src/common_ffi/common_ffi.nit b/src/common_ffi/common_ffi.nit
new file mode 100644 (file)
index 0000000..24cbcda
--- /dev/null
@@ -0,0 +1,144 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# FFI concers common between the compilers and the interpreter.
+# Offers services to compile modules using foreign code. Mainly allows
+# to wrap foreign code in Nit methods.
+module common_ffi
+
+import parser
+import modelbuilder
+
+import nitni
+
+import ffi_base
+import extern_classes
+import header_dependency
+import c
+
+redef class MModule
+       # Does this module uses the FFI?
+       var uses_ffi: Bool = false
+end
+
+redef class AModule
+       # C compilation unit for the FFI files
+       private var ffi_ccu: nullable CCompilationUnit = null
+
+       # Foreign language used in this AModule
+       private var present_languages = new HashSet[FFILanguage]
+
+       # Callbacks used locally
+       var ffi_callbacks = new HashMap[FFILanguage, Set[NitniCallback]]
+
+       # Ensures all of the general foreign code of the module has been analyzed.
+       # Manages header blocks, extern class types and foreign dependancies between modules
+       fun ensure_compile_ffi_wrapper
+       do
+               if ffi_ccu != null then return
+
+               # ready extern code compiler
+               var ffi_ccu = new CCompilationUnit
+               self.ffi_ccu = ffi_ccu
+
+               # generate code
+               for block in n_extern_code_blocks do
+                       var language = block.language
+                       assert language != null
+                       present_languages.add(language)
+                       language.compile_module_block(block, ffi_ccu, self)
+               end
+
+               ffi_ccu.header_c_base.add( "#include \"{mmodule.name}._nitni.h\"\n" )
+
+               # include dependancies FFI
+               for mod in mmodule.header_dependencies do
+                       if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.name}._ffi.h\"\n")
+               end
+
+               for nclassdef in n_classdefs do
+                       # Does it declares an extern type?
+                       if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then
+                               mmodule.uses_ffi = true
+                               var language = nclassdef.n_extern_code_block.language
+                               assert language != null
+                               present_languages.add(language)
+                               nclassdef.n_extern_code_block.language.compile_extern_class(
+                                       nclassdef.n_extern_code_block.as(not null), nclassdef, ffi_ccu, self)
+                       end
+               end
+       end
+
+       # Complete the compilation of the FFI code
+       fun finalize_ffi_wrapper(compdir: String, mainmodule: MModule)
+       do
+               ensure_compile_ffi_wrapper
+
+               for language in present_languages do if ffi_callbacks.keys.has(language) then
+                       for callback in ffi_callbacks[language] do
+                               language.compile_callback(callback, self, mainmodule, ffi_ccu.as(not null))
+                       end
+
+                       language.compile_to_files(self, compdir)
+               end
+
+               ffi_ccu.write_as_impl(self, compdir)
+               for filename in ffi_ccu.files do ffi_files.add(new ExternCFile(filename, self.c_compiler_options))
+       end
+end
+
+redef class AExternPropdef
+       private var ffi_has_been_compiled = false
+
+       # Compile the necessary wrapper around this extern method or constructor
+       fun compile_ffi_method(amodule: AModule)
+       do
+               assert n_extern_code_block != null
+
+               if ffi_has_been_compiled then return
+               ffi_has_been_compiled = true
+
+               amodule.ensure_compile_ffi_wrapper
+
+               var language = n_extern_code_block.language
+               assert language != null
+               amodule.present_languages.add(language)
+               n_extern_code_block.language.compile_extern_method(
+                       n_extern_code_block.as(not null), self, amodule.ffi_ccu.as(not null), amodule)
+       end
+end
+
+redef class VerifyNitniCallbacksPhase
+       redef fun process_npropdef(npropdef)
+       do
+               super
+
+               if not npropdef isa AExternPropdef then return
+
+               var code_block = npropdef.n_extern_code_block
+               if code_block == null then return
+
+               var lang = code_block.language
+               assert lang != null
+
+               # Associate callbacks used by an extern method to its foreign language
+               for callback in npropdef.foreign_callbacks.all do
+                       var map = npropdef.parent.parent.as(AModule).ffi_callbacks
+                       if not map.keys.has(lang) then map[lang] = new HashSet[NitniCallback]
+                       map[lang].add(callback)
+               end
+       end
+end
diff --git a/src/common_ffi/extern_classes.nit b/src/common_ffi/extern_classes.nit
new file mode 100644 (file)
index 0000000..0ab7b96
--- /dev/null
@@ -0,0 +1,134 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Manages all extern classes and their associated foreign type.
+module extern_classes
+
+import ffi_base
+
+redef class ToolContext
+       var extern_classes_typing_phase_ast: Phase = new ExternClassesTypingPhaseAst(self, [ffi_language_assignation_phase])
+
+       var extern_classes_typing_phase_model: Phase = new ExternClassesTypingPhaseModel(self, [extern_classes_typing_phase_ast, modelize_class_phase])
+end
+
+# Assigns the `ftype` to class definitions, work on the AST only
+private class ExternClassesTypingPhaseAst
+       super Phase
+
+       redef fun process_nclassdef(nclassdef)
+       do
+               if not nclassdef isa AStdClassdef then return
+
+               var code_block = nclassdef.n_extern_code_block
+               if code_block == null then return
+
+               if nclassdef.n_kwredef != null then
+                       # A redef cannot specifiy a different extern type
+                       toolcontext.error(nclassdef.location, "Only the introduction of a class can specify an extern type.")
+                       return
+               end
+
+               var ftype = code_block.language.get_ftype(code_block, nclassdef)
+               nclassdef.ftype_cache = ftype
+               nclassdef.ftype_computed = true
+       end
+end
+
+redef class AClassdef
+       private var ftype_cache: nullable ForeignType = null
+       private var ftype_computed = false
+
+       # Associated extern type when defined on this classdef
+       fun ftype: nullable ForeignType
+       do
+               return ftype_cache
+       end
+end
+
+private class ExternClassesTypingPhaseModel
+       super Phase
+
+       redef fun process_nclassdef(nclassdef)
+       do
+               if not nclassdef isa AStdClassdef then return
+
+               var mclassdef = nclassdef.mclassdef
+               var mclass = nclassdef.mclass
+
+               # We only need to do this once per class
+               if mclass.intro != mclassdef then return
+
+               if mclass.kind != extern_kind then return
+
+               mclass.compute_ftype(self)
+       end
+end
+
+redef class MClass
+       private var ftype_cache: nullable ForeignType = null
+       private var ftype_computed = false
+
+       # Extern type associated to this class according to specialisation
+       fun ftype: nullable ForeignType do return ftype_cache
+
+       redef fun ctype do return ftype_cache.ctype
+
+       # Type in C of the extern class
+       # If not defined in the intro of this class will look in super-classes
+       # FIXME this only supports type definition at intro, extend to superclasses by redef
+       private fun compute_ftype(v: ExternClassesTypingPhaseModel): nullable ForeignType
+       do
+               if ftype_computed then return ftype_cache
+               if kind != extern_kind then return null
+
+               # base case
+               if name == "Pointer" then
+                       ftype_cache = new ForeignType
+                       ftype_computed = true
+                       return ftype_cache
+               end
+
+               var intro_nclassdef = v.toolcontext.modelbuilder.mclassdef2nclassdef[intro]
+               var ftype = intro_nclassdef.ftype
+               if ftype == null then
+                       var ftype_b: nullable ForeignType = null # FIXME hack to circumvent bug where ftype is typed null
+
+                       # look in super classes
+                       for s in in_hierarchy(intro.mmodule).direct_greaters do
+                               var super_ftype = s.compute_ftype(v)
+                               if super_ftype != null then
+                                       if ftype_b == null then
+                                               ftype_b = super_ftype
+                                               continue
+                                       else
+                                               # detect conflict
+                                               if super_ftype != ftype_b then
+                                                       v.toolcontext.error(null, "Extern type conflict in {self}")
+                                                       return null
+                                               end
+                                       end
+                               end
+                       end
+
+                       ftype = ftype_b
+               end
+
+               ftype_cache = ftype
+               ftype_computed = true
+               return ftype
+       end
+end
diff --git a/src/common_ffi/ffi_base.nit b/src/common_ffi/ffi_base.nit
new file mode 100644 (file)
index 0000000..86f53ea
--- /dev/null
@@ -0,0 +1,209 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tools and utilities for language targetted FFI implementations
+module ffi_base
+
+import c_tools
+import parser
+import modelbuilder
+import nitni
+
+redef class ToolContext
+       var ffi_language_assignation_phase: Phase = new FFILanguageAssignationPhase(self, null)
+end
+
+class FFILanguageAssignationPhase
+       super Phase
+
+       # All supported languages
+       var languages = new Array[FFILanguage]
+
+       redef fun process_nmodule(nmodule)
+       do
+               for block in nmodule.n_extern_code_blocks do
+                       verify_foreign_code_on_node( block )
+               end
+       end
+
+       redef fun process_npropdef(npropdef)
+       do
+               if npropdef isa AExternPropdef then
+                       var code_block = npropdef.n_extern_code_block
+                       if code_block != null then
+                               verify_foreign_code_on_node( code_block )
+                       end
+               end
+       end
+
+       redef fun process_nclassdef(nclassdef)
+       do
+               if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then
+                       verify_foreign_code_on_node( nclassdef.n_extern_code_block.as(not null) )
+               end
+       end
+
+       private fun verify_foreign_code_on_node(n: AExternCodeBlock)
+       do
+               var found = false
+               for v in languages do
+                       var identified = v.identify_language(n)
+                       if identified then
+                               if found and identified then
+                                       toolcontext.error(n.location, "Two languages identified as possible handlers.")
+                               end
+                               n.language = v
+                               found = true
+                       end
+               end
+
+               if not found then toolcontext.error(n.location, "Unsupported language.")
+       end
+end
+
+redef class AModule
+       var ffi_files = new Array[ExternFile]
+end
+
+redef class AExternCodeBlock
+       fun language_name: nullable String do
+               if n_in_language == null then return null
+               return n_in_language.n_string.without_quotes
+       end
+       fun language_name_lowered: nullable String do
+               if language_name == null then return null
+               return language_name.to_lower
+       end
+
+       fun code: String do return n_extern_code_segment.without_guard
+
+       var language: nullable FFILanguage = null
+end
+
+# Visitor for a specific languages. Works kinda like a `Phase` and is executed
+# by a `Phase`.
+class FFILanguage
+       init(ffi_language_assignation_phase: FFILanguageAssignationPhase)
+       do
+               ffi_language_assignation_phase.languages.add(self)
+       end
+
+       # Is this `block` written in this language?
+       fun identify_language(block: AExternCodeBlock ): Bool is abstract
+
+       # Generate wrapper code for this module/header code block
+       fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, nmodule: AModule) is abstract
+
+       # Generate wrapper code for this extern method
+       fun compile_extern_method(block: AExternCodeBlock, m: AExternPropdef,
+               ecc: CCompilationUnit, nmodule: AModule) is abstract
+
+       # Generate wrapper code for this extern class
+       fun compile_extern_class(block: AExternCodeBlock, m: AClassdef,
+               ecc: CCompilationUnit, nmodule: AModule) is abstract
+
+       # Get the foreign type of this extern class definition
+       fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract
+
+       # Generate the code to offer this callback if foreign code
+       fun compile_callback(callback: NitniCallback, nmodule: AModule,
+               mainmmodule: MModule, ecc: CCompilationUnit) is abstract
+
+       # Complete compilation of generated code
+       fun compile_to_files(amodule: AModule, directory: String) do end
+end
+
+redef class TString
+       # Returns the content of this node without both of the surrounding "
+       fun without_quotes: String
+       do
+               assert text.length >= 2
+               return text.substring(1, text.length-2)
+       end
+end
+
+redef class TExternCodeSegment
+       # Returns the content of this node without the surrounding `{ and `}
+       fun without_guard: String
+       do
+               assert text.length >= 4
+               return text.substring(2, text.length-4)
+       end
+end
+
+# An extern file to compile
+class ExternFile
+       # The filename of the file
+       var filename: String
+
+       fun makefile_rule_name: String is abstract
+       fun makefile_rule_content: String is abstract
+end
+
+redef class CCompilationUnit
+       fun write_as_impl( amodule: AModule, compdir: String )
+       do
+               var base_name = "{amodule.mmodule.name}._ffi"
+
+               var h_file = "{base_name}.h"
+               var guard = "{amodule.cname.to_s.to_upper}_NIT_H"
+               write_header_to_file( amodule, "{compdir}/{h_file}", new Array[String], guard)
+
+               var c_file = "{base_name}.c"
+               write_body_to_file( amodule, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "\"{h_file}\""] )
+
+               files.add( "{compdir}/{c_file}" )
+       end
+
+       fun write_header_to_file(amodule: AModule, file: String, includes: Array[String], guard: String)
+       do
+               var stream = new OFStream.open( file )
+
+               # header comments
+               var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n"
+
+               stream.write( module_info )
+
+               stream.write( "#ifndef {guard}\n" )
+               stream.write( "#define {guard}\n\n" )
+
+               for incl in includes do stream.write( "#include {incl}\n" )
+
+               compile_header_core( stream )
+
+               # header file guard close
+               stream.write( "#endif\n" )
+               stream.close
+       end
+
+       fun write_body_to_file(amodule: AModule, file: String, includes: Array[String])
+       do
+               var stream = new OFStream.open(file)
+
+               var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n"
+
+               stream.write( module_info )
+               for incl in includes do stream.write( "#include {incl}\n" )
+
+               compile_body_core( stream )
+
+               stream.close
+       end
+end
+
+class ForeignType
+       fun ctype: String do return "void*"
+end
diff --git a/src/common_ffi/header_dependency.nit b/src/common_ffi/header_dependency.nit
new file mode 100644 (file)
index 0000000..de247e5
--- /dev/null
@@ -0,0 +1,77 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tracks which modules has public header code that must be imported
+# by user modules.
+module header_dependency
+
+import ffi_base
+import c
+
+redef class ToolContext
+       var header_dependancy_phase: Phase = new HeaderDependancyPhase(self, [ffi_language_assignation_phase, modelize_class_phase])
+end
+
+redef class AModule
+       private fun has_public_c_header: Bool do
+               for code_block in n_extern_code_blocks do if code_block.is_c_header then return true
+               return false
+       end
+end
+
+redef class MModule
+       private var header_dependencies_cache: nullable Array[MModule] = null
+       fun header_dependencies: Array[MModule]
+       do
+               var cache = header_dependencies_cache
+               assert cache != null
+               return cache
+       end
+
+       private fun compute_header_dependencies(v: HeaderDependancyPhase)
+       do
+               if header_dependencies_cache != null then return
+
+               var header_dependencies = new Array[MModule]
+
+               # gather from importation
+               for m in in_importation.direct_greaters do
+                       m.compute_header_dependencies(v)
+
+                       # does the super module has inherited dependancies?
+                       var hd = m.header_dependencies
+                       if not hd.is_empty then 
+                               header_dependencies.add_all(hd)
+                       end
+
+                       # does the super module itself has extern dependancies?
+                       var amodule = v.toolcontext.modelbuilder.mmodule2nmodule[m]
+                       if amodule.has_public_c_header then header_dependencies.add(m)
+               end
+
+               header_dependencies_cache = header_dependencies
+       end
+end
+
+private class HeaderDependancyPhase
+       super Phase
+
+       redef fun process_nmodule(nmodule)
+       do
+               var mmodule = nmodule.mmodule
+               mmodule.compute_header_dependencies(self)
+       end
+end