diff --git a/src/compiler/compiler_ffi/compiler_ffi.nit b/src/compiler/compiler_ffi/compiler_ffi.nit index 7a536b152c..e74853dfe2 100644 --- a/src/compiler/compiler_ffi/compiler_ffi.nit +++ b/src/compiler/compiler_ffi/compiler_ffi.nit @@ -87,7 +87,7 @@ redef class MExplicitCall assert mproperty isa MMethod # In nitni files, declare internal function as extern - var full_friendly_csignature = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context) + var full_friendly_csignature = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, user_c_call_context) ccu.header_decl.add("extern {full_friendly_csignature};\n") if not compile_implementation_too then return @@ -145,17 +145,16 @@ end redef class MType private fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit, compile_implementation_too: Bool) do - # actually, we do not need to do anything when using the bohem garbage collector - var call_context = from_c_call_context + var call_context = user_c_call_context # incr_ref ccu.header_decl.add "#ifndef {mangled_cname}_incr_ref\n" - ccu.header_decl.add " #define {mangled_cname}_incr_ref(from) nitni_global_ref_incr(({call_context.name_mtype(self)})(from))\n" + ccu.header_decl.add " #define {mangled_cname}_incr_ref(from) nitni_global_ref_incr(({call_context.name_mtype(self)})((void*)from))\n" ccu.header_decl.add "#endif\n" # decr_ref ccu.header_decl.add "#ifndef {mangled_cname}_decr_ref\n" - ccu.header_decl.add " #define {mangled_cname}_decr_ref(from) nitni_global_ref_decr(({call_context.name_mtype(self)})(from))\n" + ccu.header_decl.add " #define {mangled_cname}_decr_ref(from) nitni_global_ref_decr(({call_context.name_mtype(self)})((void*)from))\n" ccu.header_decl.add "#endif\n" end end @@ -169,7 +168,7 @@ redef class MNullableType var full_cname = "NIT_NULL___{base_cname}" # In nitni files, declare internal function as extern - var full_friendly_csignature = "{cname_blind} {full_cname}()" + var full_friendly_csignature = "{friendly_cname} {full_cname}()" ccu.header_decl.add("extern {full_friendly_csignature};\n") # In nitni files, #define friendly as extern @@ -204,7 +203,7 @@ redef class MExplicitSuper var mclass_type = from.mclassdef.mclass.mclass_type # In nitni files, declare internal function as extern - var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context) + var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, user_c_call_context) ccu.header_decl.add("extern {internal_csignature};\n") # In nitni files, #define friendly as extern @@ -262,7 +261,7 @@ redef class MExplicitCast # # In nitni files, declare internal function as extern - var full_friendly_csignature = "int {v.compiler.mainmodule.c_name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind})" + var full_friendly_csignature = "int {v.compiler.mainmodule.c_name}___{from.mangled_cname}_is_a_{to.mangled_cname}({from.friendly_cname})" ccu.header_decl.add("extern {full_friendly_csignature};\n") # In nitni files, #define friendly as extern @@ -301,7 +300,7 @@ redef class MExplicitCast # # In nitni files, declare internal function as extern - full_friendly_csignature = "{to.cname_blind} {v.compiler.mainmodule.c_name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})" + full_friendly_csignature = "{to.friendly_cname} {v.compiler.mainmodule.c_name }___{from.mangled_cname}_as_{to.mangled_cname}({from.friendly_cname})" ccu.header_decl.add("extern {full_friendly_csignature};\n") # In nitni files, #define friendly as extern diff --git a/src/compiler/compiler_ffi/light.nit b/src/compiler/compiler_ffi/light.nit index 1f59771ea3..8a3eae0fa1 100644 --- a/src/compiler/compiler_ffi/light.nit +++ b/src/compiler/compiler_ffi/light.nit @@ -274,9 +274,9 @@ redef class MType assert not is_cprimitive # define friendly type - ccu.header_c_types.add("#ifndef NIT_TYPE_{cname}\n") - ccu.header_c_types.add("#define NIT_TYPE_{cname} 1\n") - ccu.header_c_types.add("typedef struct nitni_instance *{cname};\n") + ccu.header_c_types.add("#ifndef NIT_TYPE_{friendly_cname}\n") + ccu.header_c_types.add("#define NIT_TYPE_{friendly_cname} 1\n") + ccu.header_c_types.add("typedef struct nit_struct{friendly_cname}_\{ struct nitni_instance *ref; \} *{friendly_cname};\n") ccu.header_c_types.add("#endif\n") end end diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 8587f4abac..e8a5d39371 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -1288,8 +1288,8 @@ class SeparateCompilerVisitor self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */") self.require_declaration("type_{mtype.c_name}") self.add("{res}->type = &type_{mtype.c_name};") - self.require_declaration("class_{mtype.c_name}") - self.add("{res}->class = &class_{mtype.c_name};") + self.require_declaration("class_{mtype.mclass.c_name}") + self.add("{res}->class = &class_{mtype.mclass.c_name};") return res else return value diff --git a/src/ffi/cpp.nit b/src/ffi/cpp.nit index 13a6e92c97..064e2a2ed4 100644 --- a/src/ffi/cpp.nit +++ b/src/ffi/cpp.nit @@ -61,7 +61,7 @@ class CPPLanguage var mproperty = m.mpropdef.mproperty # Signature of the indirection function implemented as `extern "C"` in C++ - var indirection_sig = mproperty.build_csignature(mclass_type, mmodule, "___cpp_impl_mid", long_signature, internal_call_context) + var indirection_sig = mproperty.build_csignature(mclass_type, mmodule, "___cpp_impl_mid", long_signature, user_c_call_context) ## In C file (__ffi.c) @@ -70,7 +70,7 @@ class CPPLanguage # Call the indirection function from C (___impl) var fc: CFunction = new ExternCFunction(m, mmodule) - fc.exprs.add(mproperty.build_ccall(mclass_type, mmodule, "___cpp_impl_mid", long_signature, cpp_call_context, null)) + fc.exprs.add(mproperty.build_ccall(mclass_type, mmodule, "___cpp_impl_mid", long_signature, user_c_call_context, null)) fc.exprs.add("\n") ecc.add_exported_function( fc ) @@ -114,8 +114,6 @@ class CPPLanguage mmodule.cpp_file.add_local_function( fc ) end - redef fun compile_extern_class(block, m, ecc, mmodule) do end - redef fun get_ftype(block, m) do return new ForeignCppType(block.code) redef fun compile_to_files(mmodule, compdir) @@ -225,7 +223,7 @@ private class CppCallContext end end - return mtype.cname + return mtype.friendly_cname end end diff --git a/src/ffi/java.nit b/src/ffi/java.nit index 0dd65be745..3ce1abbcd9 100644 --- a/src/ffi/java.nit +++ b/src/ffi/java.nit @@ -159,8 +159,6 @@ class JavaLanguage mmodule.callbacks_used_from_java.join m.foreign_callbacks end - redef fun compile_extern_class(block, m, ccu, mmodule) do end - redef fun get_ftype(block, m) do return new ForeignJavaType(block.code) redef fun compile_to_files(mmodule, compdir) @@ -381,7 +379,7 @@ private class ToJavaCallContext super CallContext redef fun cast_to(mtype, name) do return "({mtype.jni_type})({name})" - redef fun cast_from(mtype, name) do return "({mtype.cname})({name})" + redef fun cast_from(mtype, name) do return "({mtype.friendly_cname})({name})" redef fun name_mtype(mtype) do return mtype.jni_type end @@ -389,7 +387,7 @@ end private class FromJavaCallContext super CallContext - redef fun cast_to(mtype, name) do return "({mtype.cname})({name})" + redef fun cast_to(mtype, name) do return "({mtype.friendly_cname})({name})" redef fun cast_from(mtype, name) do return "({mtype.jni_type})({name})" redef fun name_mtype(mtype) do return mtype.jni_type end diff --git a/src/ffi/light_c.nit b/src/ffi/light_c.nit index 4c99e9b9dc..1970e0b489 100644 --- a/src/ffi/light_c.nit +++ b/src/ffi/light_c.nit @@ -46,13 +46,11 @@ class CLanguage redef fun compile_extern_method(block, m, ecc, mmodule) do var fc = new ExternCFunction(m, mmodule) - fc.decls.add( block.location.as_line_pragma ) - fc.exprs.add( block.code ) + fc.decls.add block.location.as_line_pragma + fc.exprs.add block.code ecc.body_impl.add fc.to_writer end - redef fun compile_extern_class(block, m, ecc, mmodule) do end - redef fun get_ftype(block, m) do return new ForeignCType(block.code) end @@ -71,9 +69,6 @@ redef class Location end redef class MModule - # FIXME make nullable the key of `cflags`, `ldflags` and `cppflags` when - # supported by the bootstrap - # Custom options for the C compiler (CFLAGS) var cflags = new MultiHashMap[String, String] @@ -92,31 +87,13 @@ class ForeignCType end # Context when calling user C code from generated code -fun to_c_call_context: ToCCallContext do return once new ToCCallContext - -# Context when calling generated code from user C code -fun from_c_call_context: FromCCallContext do return once new FromCCallContext - -# Context when calling user C code from generated code -class ToCCallContext - super CallContext +fun user_c_call_context: UserCCallContext do return once new UserCCallContext - # TODO: private init because singleton instance (see `to_c_call_context`) - - redef fun name_mtype(mtype) - do - if mtype isa MClassType and mtype.mclass.kind == extern_kind then return "void *" - return mtype.cname - end -end - -# Context when calling generated code from user C code -class FromCCallContext +# Context in C code written by the user +class UserCCallContext super CallContext - # TODO: private init because singleton instance (see `from_c_call_context`) - - redef fun name_mtype(mtype) do return mtype.cname + redef fun name_mtype(mtype) do return mtype.friendly_cname end class ExternCFunction @@ -129,7 +106,7 @@ class ExternCFunction self.method = method var recv_mtype = method.mpropdef.mclassdef.bound_mtype - var csignature = method.mpropdef.mproperty.build_csignature(recv_mtype, mmodule, "___impl", long_signature, from_c_call_context) + var csignature = method.mpropdef.mproperty.build_csignature(recv_mtype, mmodule, "___impl", long_signature, user_c_call_context) super( csignature ) end diff --git a/src/ffi/light_ffi.nit b/src/ffi/light_ffi.nit index 2245a922a4..7f66bb830d 100644 --- a/src/ffi/light_ffi.nit +++ b/src/ffi/light_ffi.nit @@ -100,14 +100,12 @@ redef class AModule ffi_ccu.body_decl.add("#endif\n") for nclassdef in n_classdefs do - # Does it declares an extern type? + # Is it an extern class with an extern type? if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then mmodule.uses_ffi = true var language = nclassdef.n_extern_code_block.language assert language != null mmodule.present_languages.add(language) - nclassdef.n_extern_code_block.language.compile_extern_class( - nclassdef.n_extern_code_block.as(not null), nclassdef, ffi_ccu, mmodule) end end end diff --git a/src/ffi/light_ffi_base.nit b/src/ffi/light_ffi_base.nit index 33853016ca..4ef50b177e 100644 --- a/src/ffi/light_ffi_base.nit +++ b/src/ffi/light_ffi_base.nit @@ -103,19 +103,16 @@ redef class AExternCodeBlock var language: nullable FFILanguage = null end -# Visitor for a specific languages. Works kinda like a `Phase` and is executed -# by a `Phase`. -class FFILanguage +# Visitor for a foreign language +abstract class FFILanguage + # `FFILanguageAssignationPhase` assigning `self` to `AExternCodeBlock`s var ffi_language_assignation_phase: FFILanguageAssignationPhase - init - do - ffi_language_assignation_phase.languages.add(self) - end + init do ffi_language_assignation_phase.languages.add(self) # Is this `block` written in this language? - fun identify_language(block: AExternCodeBlock ): Bool is abstract + fun identify_language(block: AExternCodeBlock): Bool is abstract # Generate wrapper code for this module/header code block fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, mmodule: MModule) is abstract @@ -124,10 +121,6 @@ class FFILanguage fun compile_extern_method(block: AExternCodeBlock, m: AMethPropdef, ecc: CCompilationUnit, nmodule: MModule) is abstract - # Generate wrapper code for this extern class - fun compile_extern_class(block: AExternCodeBlock, m: AClassdef, - ecc: CCompilationUnit, mmodule: MModule) is abstract - # Get the foreign type of this extern class definition fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract diff --git a/src/ffi/objc.nit b/src/ffi/objc.nit index 9c0545a957..c8ec76e22d 100644 --- a/src/ffi/objc.nit +++ b/src/ffi/objc.nit @@ -91,8 +91,6 @@ class ObjCLanguage mmodule.objc_file.add_exported_function fc end - redef fun compile_extern_class(block, m, ecc, mmodule) do end - redef fun get_ftype(block, m) do return new ForeignObjCType(block.code) redef fun compile_to_files(mmodule, compdir) @@ -222,7 +220,7 @@ private class ObjCCallContext end end - return mtype.cname + return mtype.friendly_cname end end diff --git a/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit b/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit index 3ac05af4c9..5540e2f75f 100644 --- a/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit +++ b/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit @@ -212,7 +212,7 @@ typedef union nit_call_arg { var used_types = collect_mtypes for t in used_types do if not t.is_cprimitive then - ecc.header_c_types.add "typedef void* {t.cname};\n" + ecc.header_c_types.add "typedef void* {t.friendly_cname};\n" end end @@ -291,7 +291,7 @@ end redef class MMethodDef # Name of the entry point to the implementation function in the foreign lib - fun foreign_lib_entry_cname: String do return "entry__{cname}" + fun foreign_lib_entry_cname: String do return "entry__{friendly_cname}" # Compile the standardized entry point as part of the foreign lib API private fun compile_foreign_code_entry(ecc: CCompilationUnit) diff --git a/src/nitni/nitni_base.nit b/src/nitni/nitni_base.nit index d18d2c0fd9..28935c4fa0 100644 --- a/src/nitni/nitni_base.nit +++ b/src/nitni/nitni_base.nit @@ -60,14 +60,14 @@ end redef class MMethodDef # Name of the function to callback this method from C, # also used in other functions names used for this method. - fun cname: String do return "{mclassdef.mclass.name}_{mproperty.short_cname}" + fun friendly_cname: String do return "{mclassdef.mclass.name}_{mproperty.short_cname}" end redef class MType # Representation of this type in pure C on the FFI extern side # Object -> Object # Pointer -> void* - fun cname: String do return cname_normal_class + fun friendly_cname: String do return cname_normal_class # Representation of this type in C for the internal of the system # Hides extern types. @@ -89,7 +89,7 @@ redef class MType end redef class MClassType - redef fun cname + redef fun friendly_cname do var name = mclass.name if name == "Bool" then return "int" diff --git a/src/nitni/nitni_callbacks.nit b/src/nitni/nitni_callbacks.nit index 823f6242ab..41c703bebc 100644 --- a/src/nitni/nitni_callbacks.nit +++ b/src/nitni/nitni_callbacks.nit @@ -42,44 +42,39 @@ class VerifyNitniCallbacksPhase end end -# Provides a better API but mainly the same content as AExternCalls +# Set of callbacks to Nit from an `AExternCodeBlock` and used Nit types class ForeignCallbackSet - # set of imported functions, cached to avoid repetitions - var callbacks: Set[ MExplicitCall ] = new HashSet[ MExplicitCall ] - - # set of imported functions, cached to avoid repetitions - var supers: Set[ MExplicitSuper ] = new HashSet[ MExplicitSuper ] - - # set of relevant types, cached to avoid repetitions - var types: Set[ MType ] = new HashSet[ MType ] - - # set of imported casts and as, cached to avoid repetitions - var casts: Set[ MExplicitCast ] = new HashSet[ MExplicitCast ] - - # Utility function, must be called only when all other maps are filled - private var all_cached: nullable Set[NitniCallback] = null - fun all: Set[NitniCallback] - do - var cached = all_cached - if cached != null then return cached - - var set = new HashSet[NitniCallback] - set.add_all(callbacks) - set.add_all(supers) - set.add_all(types) - set.add_all(casts) - - self.all_cached = set + # Callbacks to method, accessors and constructors + var callbacks = new Set[MExplicitCall] + + # Callbacks to super + var supers = new Set[MExplicitSuper] + + # Types used by the extern method signature and callbacks + var types = new Set[MType] + + # Casts to be used from foreign code + var casts = new Set[MExplicitCast] + + # All callbacks and types + # + # This attribute must be read only when all other sets are callback. + var all: Set[NitniCallback] is lazy do + var set = new Set[NitniCallback] + set.add_all callbacks + set.add_all supers + set.add_all types + set.add_all casts return set end # Integrate content from the `other` set into this one fun join(other: ForeignCallbackSet) do - callbacks.add_all( other.callbacks ) - supers.add_all( other.supers ) - types.add_all( other.types ) - casts.add_all( other.casts ) + callbacks.add_all other.callbacks + supers.add_all other.supers + types.add_all other.types + casts.add_all other.casts end end @@ -94,7 +89,7 @@ redef class AMethPropdef return fcs end - # Verifiy the validity of the explicit callbacks to Nit + # Verify the validity of the explicit callbacks to Nit # also fills the set returned by foreign_callbacks fun verify_nitni_callbacks(toolcontext: ToolContext) do @@ -208,11 +203,11 @@ class MExplicitCall var creturn_type if mproperty.is_init then - creturn_type = recv_mtype.cname + creturn_type = recv_mtype.friendly_cname else if signature.return_mtype != null then var ret_mtype = signature.return_mtype ret_mtype = ret_mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true) - creturn_type = ret_mtype.cname + creturn_type = ret_mtype.friendly_cname else creturn_type = "void" end @@ -230,11 +225,11 @@ class MExplicitCall var cparams = new List[String] if not mproperty.is_init then - cparams.add( "{recv_mtype.cname} self" ) + cparams.add( "{recv_mtype.friendly_cname} self" ) end for p in signature.mparameters do var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true) - cparams.add( "{param_mtype.cname} {p.name}" ) + cparams.add( "{param_mtype.friendly_cname} {p.name}" ) end return "{creturn_type} {cname}( {cparams.join(", ")} )" diff --git a/tests/sav/test_ffi_c_generic_extern_class_alt1.res b/tests/sav/test_ffi_c_generic_extern_class_alt1.res new file mode 100644 index 0000000000..7ffba12d5b --- /dev/null +++ b/tests/sav/test_ffi_c_generic_extern_class_alt1.res @@ -0,0 +1,2 @@ +alt/test_ffi_c_generic_extern_class_alt1.nit:34,7--11: Type Error: expected `B`, got `C`. +alt/test_ffi_c_generic_extern_class_alt1.nit:35,7--11: Type Error: expected `B`, got `C`. diff --git a/tests/test_ffi_c_generic_extern_class.nit b/tests/test_ffi_c_generic_extern_class.nit new file mode 100644 index 0000000000..eaf2c920b2 --- /dev/null +++ b/tests/test_ffi_c_generic_extern_class.nit @@ -0,0 +1,35 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# 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. + +import kernel + +extern class A[T: Pointer] + new `{ return NULL; `} + fun foo(v: T): T do return v + fun bar(v: T): T `{ return v; `} +end + +extern class B + new `{ return NULL; `} +end + +extern class C + new `{ return NULL; `} +end + +var a = new A[B] +a.foo new B +a.bar new B +#alt1#a.foo new C +#alt1#a.bar new C