Merge commit 'b7e675f'
[nit.git] / src / common_ffi / ffi_base.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Tools and utilities for language targetted FFI implementations
18 module ffi_base
19
20 import c_tools
21 import parser
22 import modelbuilder
23 import nitni
24
25 redef class ToolContext
26 var ffi_language_assignation_phase: Phase = new FFILanguageAssignationPhase(self, null)
27 end
28
29 class FFILanguageAssignationPhase
30 super Phase
31
32 # All supported languages
33 var languages = new Array[FFILanguage]
34
35 redef fun process_nmodule(nmodule)
36 do
37 for block in nmodule.n_extern_code_blocks do
38 verify_foreign_code_on_node( block )
39 end
40 end
41
42 redef fun process_npropdef(npropdef)
43 do
44 if npropdef isa AExternPropdef then
45 var code_block = npropdef.n_extern_code_block
46 if code_block != null then
47 verify_foreign_code_on_node( code_block )
48 end
49 end
50 end
51
52 redef fun process_nclassdef(nclassdef)
53 do
54 if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then
55 verify_foreign_code_on_node( nclassdef.n_extern_code_block.as(not null) )
56 end
57 end
58
59 private fun verify_foreign_code_on_node(n: AExternCodeBlock)
60 do
61 var found = false
62 for v in languages do
63 var identified = v.identify_language(n)
64 if identified then
65 if found and identified then
66 toolcontext.error(n.location, "Two languages identified as possible handlers.")
67 end
68 n.language = v
69 found = true
70 end
71 end
72
73 if not found then toolcontext.error(n.location, "Unsupported language.")
74 end
75 end
76
77 redef class AModule
78 var ffi_files = new Array[ExternFile]
79 end
80
81 redef class AExternCodeBlock
82 fun language_name: nullable String do
83 if n_in_language == null then return null
84 return n_in_language.n_string.without_quotes
85 end
86 fun language_name_lowered: nullable String do
87 if language_name == null then return null
88 return language_name.to_lower
89 end
90
91 fun code: String do return n_extern_code_segment.without_guard
92
93 var language: nullable FFILanguage = null
94 end
95
96 # Visitor for a specific languages. Works kinda like a `Phase` and is executed
97 # by a `Phase`.
98 class FFILanguage
99 init(ffi_language_assignation_phase: FFILanguageAssignationPhase)
100 do
101 ffi_language_assignation_phase.languages.add(self)
102 end
103
104 # Is this `block` written in this language?
105 fun identify_language(block: AExternCodeBlock ): Bool is abstract
106
107 # Generate wrapper code for this module/header code block
108 fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, nmodule: AModule) is abstract
109
110 # Generate wrapper code for this extern method
111 fun compile_extern_method(block: AExternCodeBlock, m: AExternPropdef,
112 ecc: CCompilationUnit, nmodule: AModule) is abstract
113
114 # Generate wrapper code for this extern class
115 fun compile_extern_class(block: AExternCodeBlock, m: AClassdef,
116 ecc: CCompilationUnit, nmodule: AModule) is abstract
117
118 # Get the foreign type of this extern class definition
119 fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract
120
121 # Generate the code to offer this callback if foreign code
122 fun compile_callback(callback: NitniCallback, nmodule: AModule,
123 mainmmodule: MModule, ecc: CCompilationUnit) is abstract
124
125 # Complete compilation of generated code
126 fun compile_to_files(amodule: AModule, directory: String) do end
127 end
128
129 redef class TString
130 # Returns the content of this node without both of the surrounding "
131 fun without_quotes: String
132 do
133 assert text.length >= 2
134 return text.substring(1, text.length-2)
135 end
136 end
137
138 redef class TExternCodeSegment
139 # Returns the content of this node without the surrounding `{ and `}
140 fun without_guard: String
141 do
142 assert text.length >= 4
143 return text.substring(2, text.length-4)
144 end
145 end
146
147 # An extern file to compile
148 class ExternFile
149 # The filename of the file
150 var filename: String
151
152 fun makefile_rule_name: String is abstract
153 fun makefile_rule_content: String is abstract
154 end
155
156 redef class CCompilationUnit
157 fun write_as_impl( amodule: AModule, compdir: String )
158 do
159 var base_name = "{amodule.mmodule.name}._ffi"
160
161 var h_file = "{base_name}.h"
162 var guard = "{amodule.cname.to_s.to_upper}_NIT_H"
163 write_header_to_file( amodule, "{compdir}/{h_file}", new Array[String], guard)
164
165 var c_file = "{base_name}.c"
166 write_body_to_file( amodule, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "\"{h_file}\""] )
167
168 files.add( "{compdir}/{c_file}" )
169 end
170
171 fun write_header_to_file(amodule: AModule, file: String, includes: Array[String], guard: String)
172 do
173 var stream = new OFStream.open( file )
174
175 # header comments
176 var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n"
177
178 stream.write( module_info )
179
180 stream.write( "#ifndef {guard}\n" )
181 stream.write( "#define {guard}\n\n" )
182
183 for incl in includes do stream.write( "#include {incl}\n" )
184
185 compile_header_core( stream )
186
187 # header file guard close
188 stream.write( "#endif\n" )
189 stream.close
190 end
191
192 fun write_body_to_file(amodule: AModule, file: String, includes: Array[String])
193 do
194 var stream = new OFStream.open(file)
195
196 var module_info = "/*\n\tExtern implementation of Nit module {amodule.mmodule.name}\n*/\n"
197
198 stream.write( module_info )
199 for incl in includes do stream.write( "#include {incl}\n" )
200
201 compile_body_core( stream )
202
203 stream.close
204 end
205 end
206
207 class ForeignType
208 fun ctype: String do return "void*"
209 end