nitunit: updade usage of get_mmodule_by_name
[nit.git] / src / common_ffi / cpp.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 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 # Supports the use of the C++ language through the FFI
18 module cpp
19
20 import extern_classes
21 import c
22
23 redef class FFILanguageAssignationPhase
24 var cpp_language: FFILanguage = new CPPLanguage(self)
25 end
26
27 redef class AModule
28 private var cpp_file: nullable CPPCompilationUnit = null
29 end
30
31 redef class MModule
32 var cpp_compiler_options writable = ""
33 end
34
35 class CPPLanguage
36 super FFILanguage
37
38 redef fun identify_language(n) do return n.is_cpp
39
40 redef fun compile_module_block(block, ecc, nmodule)
41 do
42 if nmodule.cpp_file == null then nmodule.cpp_file = new CPPCompilationUnit
43
44 if block.is_cpp_header then
45 nmodule.cpp_file.header_custom.add(block.location.as_line_pragma)
46 nmodule.cpp_file.header_custom.add(block.code)
47 else if block.is_cpp_body then
48 nmodule.cpp_file.body_custom.add( block.location.as_line_pragma )
49 nmodule.cpp_file.body_custom.add( block.code )
50 end
51 end
52
53 # We call C++ from C using 2 more files (_ffi.c and _ffi.cpp) and multiple generated functions:
54 # 1. The standard C implementation function (___impl) expected by the common FFI
55 # 2. The indirection function (___cpp_impl_mid) is a C function, called from C but implemented as `extern "C"` in C++
56 # 3. The actual C++ implementation function (___cpp_impl)
57 redef fun compile_extern_method(block, m, ecc, nmodule)
58 do
59 if nmodule.cpp_file == null then nmodule.cpp_file = new CPPCompilationUnit
60
61 var mmodule = nmodule.mmodule.as(not null)
62 var mclass_type = m.parent.as(AClassdef).mclass.mclass_type
63 var mproperty = m.mpropdef.mproperty
64
65 # Signature of the indirection function implemented as `extern "C"` in C++
66 var indirection_sig = mproperty.build_csignature(mclass_type, mmodule, "___cpp_impl_mid", long_signature, internal_call_context)
67
68 ## In C file (__ffi.c)
69
70 # Declare the indirection function in C
71 ecc.body_decl.add("{indirection_sig};\n")
72
73 # Call the indirection function from C (___impl)
74 var fc: CFunction = new ExternCFunction(m, mmodule)
75 fc.exprs.add(mproperty.build_ccall(mclass_type, mmodule, "___cpp_impl_mid", long_signature, cpp_call_context, null))
76 fc.exprs.add("\n")
77 ecc.add_exported_function( fc )
78
79 ## In C++ file (__ffi.cpp)
80
81 # Declare the indirection function in C++
82 nmodule.cpp_file.header_decl.add("extern \"C\" \{\n")
83 nmodule.cpp_file.header_decl.add("{indirection_sig};\n")
84 nmodule.cpp_file.header_decl.add("\}\n")
85
86 # Implement the indirection function as extern in C++
87 # Will convert C arguments to C++ and call the C++ implementation function.
88 fc = new CFunction(indirection_sig)
89 if not mproperty.is_init then
90 var param_name = "recv"
91 var type_name = to_cpp_call_context.name_mtype(mclass_type)
92 if mclass_type.mclass.ftype isa ForeignCppType then
93 fc.exprs.add("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
94 else
95 fc.exprs.add("{type_name} {param_name}_for_cpp = {param_name};\n")
96 end
97 end
98 for param in m.mpropdef.msignature.mparameters do
99 var param_name = param.name
100 var type_name = to_cpp_call_context.name_mtype(param.mtype)
101 if mclass_type.mclass.ftype isa ForeignCppType then
102 fc.exprs.add("{type_name} {param_name}_for_cpp = static_cast<{type_name}>({param_name});\n")
103 else
104 fc.exprs.add("{type_name} {param_name}_for_cpp = {param_name};\n")
105 end
106 end
107 fc.exprs.add(mproperty.build_ccall(mclass_type, mmodule, "___cpp_impl", long_signature, cpp_call_context, "_for_cpp"))
108 fc.exprs.add("\n")
109 nmodule.cpp_file.add_local_function(fc)
110
111 # Custom C++, the body of the Nit C++ method is copied to its own C++ function
112 var cpp_signature = mproperty.build_csignature(mclass_type, mmodule, "___cpp_impl", long_signature, cpp_call_context)
113 fc = new CFunction(cpp_signature)
114 fc.decls.add( block.location.as_line_pragma )
115 fc.exprs.add( block.code )
116 nmodule.cpp_file.add_local_function( fc )
117 end
118
119 redef fun compile_extern_class(block, m, ecc, nmodule) do end
120
121 redef fun get_ftype(block, m) do return new ForeignCppType(block.code)
122
123 redef fun compile_to_files(nmodule, compdir)
124 do
125 var cpp_file = nmodule.cpp_file
126 assert cpp_file != null
127
128 # write .cpp and .hpp file
129 cpp_file.header_custom.add("extern \"C\" \{\n")
130 cpp_file.header_custom.add("#include \"{nmodule.mmodule.name}._ffi.h\"\n")
131 cpp_file.header_custom.add("\}\n")
132
133 var file = cpp_file.write_to_files(nmodule, compdir)
134
135 # add complation to makefile
136 nmodule.ffi_files.add(file)
137
138 # add linked option to support C++
139 nmodule.mmodule.c_linker_options = "{nmodule.mmodule.c_linker_options} -lstdc++"
140 end
141
142 redef fun compile_callback(callback, nmodule, mmodule, ecc)
143 do
144 callback.compile_callback_to_cpp(nmodule, mmodule)
145 end
146 end
147
148 redef class AExternCodeBlock
149 fun is_cpp : Bool do return language_name != null and
150 (language_name_lowered == "c++" or language_name_lowered.has_prefix("c++ "))
151
152 fun is_cpp_body : Bool do return language_name != null and
153 (language_name_lowered == "c++" or language_name_lowered == "c++ body")
154
155 fun is_cpp_header : Bool do return language_name != null and
156 (language_name_lowered == "c++ header")
157 end
158
159 class CPPCompilationUnit
160 super CCompilationUnit
161
162 fun write_to_files(amodule: AModule, compdir: String): ExternCppFile
163 do
164 var mmodule = amodule.mmodule.as(not null)
165 var base_name = "{mmodule.name}._ffi"
166
167 var h_file = "{base_name}.hpp"
168 var guard = "{amodule.cname.to_s.to_upper}_NIT_HPP"
169
170 write_header_to_file(amodule, "{compdir}/{h_file}", new Array[String], guard)
171
172 var c_file = "{base_name}.cpp"
173 write_body_to_file(amodule, "{compdir}/{c_file}", ["<string>", "<iostream>", "\"{h_file}\""])
174
175 files.add("{compdir}/{c_file}")
176
177 return new ExternCppFile("{compdir}/{c_file}", mmodule)
178 end
179 end
180
181 class ExternCppFile
182 super ExternFile
183
184 var mmodule: MModule
185 init(path: String, mmodule: MModule)
186 do
187 super
188 self.mmodule = mmodule
189 end
190
191 redef fun makefile_rule_name do return "{filename.basename("")}.o"
192 redef fun makefile_rule_content do return "g++ {mmodule.cpp_compiler_options} -c {filename.basename("")} -o {filename.basename("")}.o"
193 end
194
195 class ForeignCppType
196 super ForeignType
197
198 var cpp_type: String
199
200 init (cpp_type: String)
201 do
202 self.cpp_type = cpp_type
203 end
204 end
205
206 redef class NitniCallback
207 fun compile_callback_to_cpp(nmodule: AModule, mmodule: MModule) do end
208 end
209
210 redef class Object
211 private fun cpp_call_context: CppCallContext do return once new CppCallContext
212 private fun to_cpp_call_context: ToCppCallContext do return once new ToCppCallContext
213 private fun from_cpp_call_context: FromCppCallContext do return once new FromCppCallContext
214 end
215
216 redef class MExplicitCall
217 redef fun compile_callback_to_cpp(nmodule, mmodule)
218 do
219 var mproperty = mproperty
220 assert mproperty isa MMethod
221
222 var cpp_signature = mproperty.build_csignature(recv_mtype, mmodule, null, short_signature, from_cpp_call_context)
223 var ccall = mproperty.build_ccall(recv_mtype, mmodule, null, long_signature, from_cpp_call_context, null)
224 var fc = new CFunction(cpp_signature)
225 fc.exprs.add(ccall)
226 nmodule.cpp_file.add_local_function( fc )
227 end
228 end
229
230 private class CppCallContext
231 super CallContext
232
233 redef fun name_mtype(mtype)
234 do
235 if mtype isa MClassType then
236 var ftype = mtype.mclass.ftype
237 if ftype isa ForeignCppType then
238 return ftype.cpp_type
239 end
240 end
241
242 return mtype.cname
243 end
244 end
245
246 class ToCppCallContext
247 super CppCallContext
248
249 redef fun cast_to(mtype, name)
250 do
251 if mtype isa MClassType and mtype.mclass.ftype isa ForeignCppType then
252 return "(void*)({name})"
253 else return name
254 end
255 end
256
257 private class FromCppCallContext
258 super CppCallContext
259
260 redef fun cast_from(mtype, name)
261 do
262 if mtype isa MClassType and mtype.mclass.ftype isa ForeignCppType then
263 return "static_cast<{name_mtype(mtype)}>({name})"
264 else return name
265 end
266 end