nitc: FFI and nitni use MModule::c_name
[nit.git] / src / ffi / objc.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 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 # FFI support for Objective-C
18 #
19 # Compiles all Objective-C code with clang. The user module must define
20 # the framework used.
21 #
22 # This module is heavily based on the C++ FFI.
23 module objc
24
25 import extern_classes
26 import c
27
28 redef class FFILanguageAssignationPhase
29 # The Objective-C language visitor
30 var objc_language: FFILanguage = new ObjCLanguage(self)
31 end
32
33 redef class MModule
34 private var objc_file: nullable ObjCCompilationUnit = null
35 end
36
37 # The Objective-C langugage visitor
38 class ObjCLanguage
39 super FFILanguage
40
41 redef fun identify_language(n) do return n.is_objc
42
43 redef fun compile_module_block(block, ecc, mmodule)
44 do
45 if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
46
47 if block.is_objc_header then
48 mmodule.objc_file.header_custom.add block.location.as_line_pragma
49 mmodule.objc_file.header_custom.add block.code
50 else if block.is_objc_body then
51 mmodule.objc_file.body_custom.add block.location.as_line_pragma
52 mmodule.objc_file.body_custom.add block.code
53 end
54 end
55
56 redef fun compile_extern_method(block, m, ecc, mmodule)
57 do
58 if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
59
60 var mpropdef = m.mpropdef
61 var recv_mtype = mpropdef.mclassdef.bound_mtype
62 var csignature = mpropdef.mproperty.build_csignature(
63 recv_mtype, mmodule, "___impl", long_signature, from_objc_call_context)
64
65 var fc = new CFunction(csignature)
66 fc.decls.add block.location.as_line_pragma
67 fc.exprs.add block.code
68 mmodule.objc_file.add_exported_function fc
69 end
70
71 redef fun compile_extern_class(block, m, ecc, mmodule) do end
72
73 redef fun get_ftype(block, m) do return new ForeignObjCType(block.code)
74
75 redef fun compile_to_files(mmodule, compdir)
76 do
77 var objc_file = mmodule.objc_file
78 assert objc_file != null
79
80 # write .m and _m.h file
81 mmodule.objc_file.header_c_types.add """
82 #include "{{{mmodule.c_name}}}._ffi.h"
83 """
84
85 var file = objc_file.write_to_files(mmodule, compdir)
86
87 # add compilation to makefile
88 mmodule.ffi_files.add file
89 end
90
91 redef fun compile_callback(callback, mmodule, mainmodule, ecc)
92 do
93 callback.compile_callback_to_objc(mmodule, mainmodule)
94 end
95 end
96
97 redef class AExternCodeBlock
98 # Is this Objective-C code?
99 fun is_objc : Bool do return language_name != null and
100 (language_name_lowered == "objc" or language_name_lowered.has_prefix("objc "))
101
102 # Is this Objective-C code for the body file?
103 fun is_objc_body : Bool do return language_name != null and
104 (language_name_lowered == "objc" or language_name_lowered == "objc body")
105
106 # Is this Objective-C code for the header file?
107 fun is_objc_header : Bool do return language_name != null and
108 (language_name_lowered == "objc header")
109 end
110
111 private class ObjCCompilationUnit
112 super CCompilationUnit
113
114 # Write this compilation unit to Objective-C source files
115 fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile
116 do
117 var base_name = "{mmodule.c_name}._ffi"
118
119 var h_file = "{base_name}_m.h"
120 var guard = "{mmodule.c_name.to_upper}_NIT_OBJC_H"
121 write_header_to_file(mmodule, compdir/h_file, new Array[String], guard)
122
123 var c_file = "{base_name}.m"
124 write_body_to_file(mmodule, compdir/c_file, ["\"{h_file}\""])
125
126 files.add compdir/c_file
127
128 mmodule.c_linker_options = "{mmodule.c_linker_options} -lobjc"
129
130 return new ExternObjCFile(compdir/c_file, mmodule)
131 end
132 end
133
134 # A Objective-C file
135 class ExternObjCFile
136 super ExternFile
137
138 # Associated `MModule`
139 var mmodule: MModule
140
141 redef fun makefile_rule_name do return "{filename.basename(".m")}_m.o"
142 redef fun makefile_rule_content do
143 return "clang $(CFLAGS) -c {filename.basename("")} -o {makefile_rule_name}"
144 end
145 redef fun compiles_to_o_file do return true
146 end
147
148 # An Objective-C type
149 class ForeignObjCType
150 super ForeignType
151
152 # Type name
153 var objc_type: String
154 end
155
156 redef class NitniCallback
157 # Compile this callback to be callable from Objective-C
158 fun compile_callback_to_objc(mmodule: MModule, mainmodule: MModule) do end
159 end
160
161 redef class MExplicitCall
162 redef fun compile_callback_to_objc(mmodule, mainmodule)
163 do
164 var mproperty = mproperty
165 assert mproperty isa MMethod
166
167 var objc_signature = mproperty.build_csignature(recv_mtype, mainmodule, null, short_signature, from_objc_call_context)
168 var ccall = mproperty.build_ccall(recv_mtype, mainmodule, null, long_signature, from_objc_call_context, null)
169 var fc = new CFunction(objc_signature)
170 fc.exprs.add ccall
171 mmodule.objc_file.add_local_function fc
172 end
173 end
174
175 # Calls withing Objective-C code
176 private fun objc_call_context: ObjCCallContext do return once new ObjCCallContext
177
178 # Calls from C to Objective-C
179 private fun to_objc_call_context: ToObjCCallContext do return once new ToObjCCallContext
180
181 # Calls from Objective-C to C
182 private fun from_objc_call_context: FromObjCCallContext do return once new FromObjCCallContext
183
184 private class ObjCCallContext
185 super CallContext
186
187 redef fun name_mtype(mtype)
188 do
189 if mtype isa MClassType then
190 var ftype = mtype.mclass.ftype
191 if ftype isa ForeignObjCType then
192 return ftype.objc_type
193 end
194 end
195
196 return mtype.cname
197 end
198 end
199
200 private class ToObjCCallContext
201 super ObjCCallContext
202
203 redef fun cast_to(mtype, name)
204 do
205 if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
206 return "(void*)({name})"
207 else return name
208 end
209 end
210
211 private class FromObjCCallContext
212 super ObjCCallContext
213
214 redef fun cast_from(mtype, name)
215 do
216 if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
217 return "({name_mtype(mtype)})({name})"
218 else return name
219 end
220 end