# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2011 Alexis Laferrière # # 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. # Stub generator for modules using the native interface module nits import parser import syntax import metamodel import analysis import abstracttool import native_interface class NitStubGenerator super AbstractCompiler var opt_in_place: OptionBool = new OptionBool("Generate stub files as .nit.[ch] instead of .stub.nit.[ch]", "-i", "--in-place") init do super( "nits" ) option_context.add_option(opt_in_place) end redef fun perform_work(mods) do for mod in mods do assert mod isa MMSrcModule if mod.is_extern_hybrid then var visitor = new StubVisitor( opt_in_place.value ) # actually compile stub mod.generate_stub( visitor ) # write to file var mod_base_path = "{mod.directory.path}/{mod.name}" visitor.write_to_files( mod_base_path ) end end end end redef class MMModule fun generate_stub( v: StubVisitor ) do ### header comments var module_info = "/*\n\tExtern implementation of Nit module {name}\n" v.header_head.add( module_info ) v.body_head.add( module_info ) # instructions to rename if not v.in_place then v.header_head.add( "\tFile initially created by nits to implement extern methods body\n" ) v.body_head.add( "\tFile initially created by nits to customize type of extern classes\n" ) end v.header_head.add( "*/\n\n" ) v.body_head.add( "*/\n\n" ) ### end of header comments # header file guard var guard = "{name.to_s.to_upper}_NIT_H" v.header_head.add( "#ifndef {guard}\n#define {guard}\n\n" ) ### imports # import standard .nit.h file v.body_head.add( "#include \"{name}.nit.h\"\n" ) # import autogenerated frontier header file v.header_head.add( "#include <{name}._nitni.h>\n\n" ) ### end of import for local_class in local_classes do ### extern methods for prop in local_class.local_local_properties do # if defined of redefined in this module # and is extern if prop.mmmodule == self and prop isa MMSrcMethod and prop.is_extern then prop.generate_stub( v ) end end ### extern classes # if class is extern and defined here first if local_class.global.intro == local_class and local_class.global.is_extern then local_class.generate_stub( v ) end end # header file guard close v.header_decl.add( "\n#endif\n" ) end end redef class MMSrcMethod fun generate_stub( v: StubVisitor ) do ### documentation to guide the user var helper_documentation = new Writer # title comment helper_documentation.add( "C implementation of {full_name}" ) # TODO add nitdoc comment # explicit extern calls signatures in comment var imported_signatures = new Array[String] for extern_call in explicit_imports do var meth = extern_call.method imported_signatures.add( "\t{meth.friendly_csignature(extern_call.local_class)} for {meth.full_name}" ) end # explicit extern casts for cast in explicit_casts do if not ( cast.is_about_nullable_only and cast.is_not_null_to_nullable ) then imported_signatures.add( "\t{cast.is_a_friendly_csignature} to check if a {cast.from} is a {cast.to}" ) end imported_signatures.add( "\t{cast.as_friendly_csignature} to cast from {cast.from} to {cast.to}" ) end # explicit extern super if need_super then imported_signatures.add( "\t{friendly_super_csignature} to call super" ) end if imported_signatures.length > 0 then helper_documentation.add("\n\nImported methods signatures:\n" ) helper_documentation.add_all( imported_signatures, "\n") end v.body_impl.add( "\n/*\n" ) v.body_impl.append( helper_documentation ) v.body_impl.add( "\n*/\n" ) ### end of documentation ### extern method implementation v.header_decl.add( "{impl_csignature};\n" ) v.body_impl.add( "{impl_csignature}\n\{\n\}\n" ) ### end of implementation end end redef class MMLocalClass fun generate_stub( v: StubVisitor ) do v.header_head.add( "#define {get_type.friendly_extern_name} void*\n" ) end end class StubVisitor # comments, imports (auto and custom from inline), types var header_head : Writer = new Writer # implementation declaration for extern methods var header_decl : Writer = new Writer # comments, imports and custom code from inline var body_head : Writer = new Writer # implementation body of extern methods var body_impl : Writer = new Writer # generates final .nit.[ch] instead of stub files .sub.nit.[ch] var in_place : Bool init ( in_place : Bool ) do self.in_place = in_place # write stubs to disk fun write_to_files( base_path : String ) do var mid_ext = if in_place then ".nit" else ".stub.nit" var stream = new OFStream.open( "{base_path}{mid_ext}.h" ) header_head.write_to_stream( stream ) header_decl.write_to_stream( stream ) stream.close stream = new OFStream.open( "{base_path}{mid_ext}.c" ) body_head.write_to_stream( stream ) body_impl.write_to_stream( stream ) stream.close end end var nits = new NitStubGenerator nits.exec_cmd_line