1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # FFI support for Objective-C
19 # Compiles all Objective-C code with clang. The user module must define
22 # This module is heavily based on the C++ FFI.
28 redef class FFILanguageAssignationPhase
29 # The Objective-C language visitor
30 var objc_language
: FFILanguage = new ObjCLanguage(self)
34 private var objc_file
: nullable ObjCCompilationUnit = null
36 private var has_public_objc_header
= false
38 # Imported modules with public Objective-C code blocks
39 var objc_imported_headers
: HashSet[MModule] is lazy
do
40 var dep
= new HashSet[MModule]
42 # gather from importation
43 for m
in in_importation
.direct_greaters
do
44 # does the super module has inherited dependencies?
45 var import_dep
= m
.objc_imported_headers
46 if not import_dep
.is_empty
then
47 dep
.add_all import_dep
50 # does the super module itself has a public header?
51 if m
.has_public_objc_header
then dep
.add
(m
)
58 # The Objective-C langugage visitor
62 redef fun identify_language
(n
) do return n
.is_objc
64 redef fun compile_module_block
(block
, ecc
, mmodule
)
66 if mmodule
.objc_file
== null then mmodule
.objc_file
= new ObjCCompilationUnit
68 if block
.is_objc_header
then
69 mmodule
.objc_file
.header_custom
.add block
.location
.as_line_pragma
70 mmodule
.objc_file
.header_custom
.add block
.code
72 mmodule
.has_public_objc_header
= true
73 else if block
.is_objc_body
then
74 mmodule
.objc_file
.body_custom
.add block
.location
.as_line_pragma
75 mmodule
.objc_file
.body_custom
.add block
.code
79 redef fun compile_extern_method
(block
, m
, ecc
, mmodule
)
81 if mmodule
.objc_file
== null then mmodule
.objc_file
= new ObjCCompilationUnit
83 var mpropdef
= m
.mpropdef
84 var recv_mtype
= mpropdef
.mclassdef
.bound_mtype
85 var csignature
= mpropdef
.mproperty
.build_csignature
(
86 recv_mtype
, mmodule
, "___impl", long_signature
, from_objc_call_context
)
88 var fc
= new CFunction(csignature
)
89 fc
.decls
.add block
.location
.as_line_pragma
90 fc
.exprs
.add block
.code
91 mmodule
.objc_file
.add_exported_function fc
94 redef fun compile_extern_class
(block
, m
, ecc
, mmodule
) do end
96 redef fun get_ftype
(block
, m
) do return new ForeignObjCType(block
.code
)
98 redef fun compile_to_files
(mmodule
, compdir
)
100 var objc_file
= mmodule
.objc_file
101 assert objc_file
!= null
103 # Import public Objective-C header of imported modules
104 var dep
= mmodule
.objc_imported_headers
106 objc_file
.header_custom
.add
"#include \"{mod.c_name}._ffi_m
.h\
"\n"
109 # write .m and _m.h file
110 mmodule
.objc_file
.header_c_types
.add
"""
111 #include "{{{mmodule.c_name}}}._ffi.h"
114 var file
= objc_file
.write_to_files
(mmodule
, compdir
)
116 # add compilation to makefile
117 mmodule
.ffi_files
.add file
120 redef fun compile_callback
(callback
, mmodule
, mainmodule
, ecc
)
122 callback
.compile_callback_to_objc
(mmodule
, mainmodule
)
126 redef class AExternCodeBlock
127 # Is this Objective-C code?
128 fun is_objc
: Bool do return language_name
!= null and
129 (language_name_lowered
== "objc" or language_name_lowered
.has_prefix
("objc "))
131 # Is this Objective-C code for the body file?
132 fun is_objc_body
: Bool do return language_name
!= null and
133 (language_name_lowered
== "objc" or language_name_lowered
== "objc body")
135 # Is this Objective-C code for the header file?
136 fun is_objc_header
: Bool do return language_name
!= null and
137 (language_name_lowered
== "objc header")
140 private class ObjCCompilationUnit
141 super CCompilationUnit
143 # Write this compilation unit to Objective-C source files
144 fun write_to_files
(mmodule
: MModule, compdir
: String): ExternObjCFile
146 var base_name
= "{mmodule.c_name}._ffi"
148 var h_file
= "{base_name}_m.h"
149 var guard
= "{mmodule.c_name.to_upper}_NIT_OBJC_H"
150 write_header_to_file
(mmodule
, compdir
/h_file
, new Array[String], guard
)
152 var c_file
= "{base_name}.m"
153 write_body_to_file
(mmodule
, compdir
/c_file
, ["\"{h_file}\
""])
155 files
.add compdir
/c_file
157 mmodule
.ldflags
.add_one
("", "-lobjc")
159 return new ExternObjCFile(compdir
/c_file
, mmodule
)
167 # Associated `MModule`
170 redef fun makefile_rule_name
do return "{filename.basename(".m")}_m.o"
171 redef fun makefile_rule_content
do
172 return "clang $(CFLAGS) -c {filename.basename} -o {makefile_rule_name}"
174 redef fun compiles_to_o_file
do return true
177 # An Objective-C type
178 class ForeignObjCType
182 var objc_type
: String
185 redef class NitniCallback
186 # Compile this callback to be callable from Objective-C
187 fun compile_callback_to_objc
(mmodule
: MModule, mainmodule
: MModule) do end
190 redef class MExplicitCall
191 redef fun compile_callback_to_objc
(mmodule
, mainmodule
)
193 var mproperty
= mproperty
194 assert mproperty
isa MMethod
196 var objc_signature
= mproperty
.build_csignature
(recv_mtype
, mainmodule
, null, short_signature
, from_objc_call_context
)
197 var ccall
= mproperty
.build_ccall
(recv_mtype
, mainmodule
, null, long_signature
, from_objc_call_context
, null)
198 var fc
= new CFunction(objc_signature
)
200 mmodule
.objc_file
.add_local_function fc
204 # Calls withing Objective-C code
205 private fun objc_call_context
: ObjCCallContext do return once
new ObjCCallContext
207 # Calls from C to Objective-C
208 private fun to_objc_call_context
: ToObjCCallContext do return once
new ToObjCCallContext
210 # Calls from Objective-C to C
211 private fun from_objc_call_context
: FromObjCCallContext do return once
new FromObjCCallContext
213 private class ObjCCallContext
216 redef fun name_mtype
(mtype
)
218 if mtype
isa MClassType then
219 var ftype
= mtype
.mclass
.ftype
220 if ftype
isa ForeignObjCType then
221 return ftype
.objc_type
229 private class ToObjCCallContext
230 super ObjCCallContext
232 redef fun cast_to
(mtype
, name
)
234 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignObjCType then
235 return "(void*)({name})"
240 private class FromObjCCallContext
241 super ObjCCallContext
243 redef fun cast_to
(mtype
, name
)
245 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignObjCType then
246 return "(__bridge void*)({name})"
250 redef fun cast_from
(mtype
, name
)
252 if mtype
isa MClassType and mtype
.mclass
.ftype
isa ForeignObjCType then
253 return "({name_mtype(mtype)})({name})"