Merge: nitdoc: Escape paths passed to the shell.
authorJean Privat <jean@pryen.org>
Mon, 10 Nov 2014 16:57:30 +0000 (11:57 -0500)
committerJean Privat <jean@pryen.org>
Mon, 10 Nov 2014 16:57:30 +0000 (11:57 -0500)
While investigating for a possible source of the recent Jenkins crashes when generating documentation, I found that Nitdoc was not escaping paths in its shell commands properly. So, this PR fix that mistake.

Signed-off-by: Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>

Pull-Request: #893
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

17 files changed:
contrib/pep8analysis/Makefile
examples/pnacl/converter/Makefile
examples/pnacl/converter/README
lib/cartesian.nit [new file with mode: 0644]
lib/combinations.nit [new file with mode: 0644]
lib/standard/collection/array.nit
lib/standard/math.nit
src/compiler/compiler_ffi.nit
tests/sav/test_ffi_c_duplicated_callback_a.res [new file with mode: 0644]
tests/sav/test_ffi_c_duplicated_callback_b.res [new file with mode: 0644]
tests/sav/test_ffi_cpp_duplicated_callback_a.res [new file with mode: 0644]
tests/sav/test_ffi_cpp_duplicated_callback_b.res [new file with mode: 0644]
tests/sav/test_new_native_alt1.res
tests/test_ffi_c_duplicated_callback_a.nit [new file with mode: 0644]
tests/test_ffi_c_duplicated_callback_b.nit [new file with mode: 0644]
tests/test_ffi_cpp_duplicated_callback_a.nit [new file with mode: 0644]
tests/test_ffi_cpp_duplicated_callback_b.nit [new file with mode: 0644]

index 6829411..c2f2669 100644 (file)
@@ -1,6 +1,6 @@
 bin/pep8analysis:
        mkdir -p bin
-       ../../bin/nitg --global -o bin/pep8analysis src/pep8analysis.nit
+       ../../bin/nitg -o bin/pep8analysis src/pep8analysis.nit
 
 doc/index.html:
        ../../bin/nitdoc src/pep8analysis.nit
index 06318b6..aee71e8 100644 (file)
@@ -1,5 +1,5 @@
 default: 
-       ../../../bin/nitg --global converter.nit
+       ../../../bin/nitg --semi-global converter.nit
 
 HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py
 serve:
index 220bc8d..fa70fff 100644 (file)
@@ -5,9 +5,9 @@ Steps to make the example work :
 2. Declare the environment variable NACL_SDK_ROOT as the root of the target platform within the SDK (ex: ~/nacl_sdk/pepper_34/) :
        $ export NACL_SDK_ROOT=/path/to/nacl_sdk/pepper_[your_version]
 
-3. Compile the Nit code with: `nitg --global converter.nit` or `make`.
+3. Compile the Nit code with: `nitg --semi-global converter.nit` or `make`.
 
-You must use the '--global' option. Some features in the standard library are not supported by the NaCL platform, the global compiler do not try to compile them.
+You must use the '--semi-global' (or `--global`) option. Some features in the standard library are not supported by the NaCL platform, the global compiler do not try to compile them.
 
 4. Start a local server using: `make serve`.
 
diff --git a/lib/cartesian.nit b/lib/cartesian.nit
new file mode 100644 (file)
index 0000000..7090050
--- /dev/null
@@ -0,0 +1,181 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# Cartesian products on heterogeneous collections.
+#
+# This module is a proof-of-concept to propose memory-efficient views on collections.
+#
+# This is a specific alternative to `combinations`, that focuses only highly efficient
+# Cartesian products between collections of different types.
+#
+#    Collection[Int] X Collection[String] -> Collection[(Int,String)]
+#
+# However, in Nit, there in no native *tuple* type.
+# So we need a first building block, a pair.
+
+# A simple read-only pair of two elements `e` and `f`.
+class Pair[E, F]
+       # The first element of the pair
+       var e: E
+
+       # The second element of the pair
+       var f: F
+
+       # The parenthesized notation.
+       #
+       # ~~~
+       # var p = new Pair[Int, String](1, "hello")
+       # assert p.to_s == "(1,hello)"
+       # ~~~
+       redef fun to_s
+       do
+               var es = e or else ""
+               var fs = f or else ""
+               return "({es},{fs})"
+       end
+
+       # Untyped pair equality.
+       #
+       # ~~~
+       # var p1 = new Pair[Object, Object](1, 2)
+       # var p2 = new Pair[Int, Int](1, 2)
+       # var p3 = new Pair[Int, Int](1, 3)
+       #
+       # assert p1 == p2
+       # assert p2 != p3
+       # ~~~
+       #
+       # Untyped because we want that `p1 == p2` above.
+       # So the method just ignores the real types of `E` and `F`.
+       redef fun ==(o) do return o isa Pair[nullable Object, nullable Object] and e == o.e and f == o.f
+
+       redef fun hash do return e.hash * 13 + f.hash * 27 # Magic numbers are magic!
+end
+
+# A view of a Cartesian-product collection over two collections.
+#
+# A Cartesian product over two collections is just a collection of pairs.
+# Therefore, this view *contains* all the pairs of elements constructed by associating each
+# element of the first collection to each element of the second collection.
+#
+# However the view is memory-efficient and the pairs are created only when needed.
+#
+# A simple Cartesian product
+# ~~~~
+# var c1 = [1,2]
+# var c2 = ["a","b","c"]
+# var c12 = new Cartesian[Int,String](c1, c2)
+# assert c12.length    == 6
+# assert c12.join(";") == "(1,a);(1,b);(1,c);(2,a);(2,b);(2,c)" # All the 6 pairs
+# ~~~~
+#
+# Note: because it is a view, changes on the base collections are reflected on the view.
+#
+# E.g. c12 is a view on c1 and c2, so if c1 changes, then c12 "changes".
+# ~~~~
+# assert c2.pop        == "c"
+# assert c12.length    == 4
+# assert c12.join(";") == "(1,a);(1,b);(2,a);(2,b)" # All the 4 remaining pairs
+# ~~~~
+#
+# Cartesian objects are collections, so can be used to build another Cartesian object.
+# ~~~~
+# var c3 = [1000..2000[
+# var c123 = new Cartesian[Pair[Int,String],Int](c12, c3)
+# assert c123.length   == 4000
+# ~~~~
+#
+# All methods of Collection are inherited, it is so great!
+#
+# E.g. search elements?
+# ~~~~
+# var p12 = new Pair[Int,String](2,"b")
+# assert c12.has(p12)      == true
+# var p123 = new Pair[Pair[Int, String], Int](p12, 1500)
+# var p123bis = new Pair[Pair[Int, String], Int](p12, 0)
+# assert c123.has(p123)    == true
+# assert c123.has(p123bis) == false
+# ~~~~
+class Cartesian[E, F]
+       super Collection[Pair[E,F]]
+
+       # The first collection
+       var ce: Collection[E]
+
+       # The second collection
+       var cf: Collection[F]
+
+       redef fun length do return ce.length * cf.length # optional, but so efficient...
+
+       redef fun iterator do return new CartesianIterator[E,F](self)
+
+       # Returns a new Cartesian where the first collection is the second.
+       # Because the full collection is virtual, the operation is cheap!
+       fun swap: Cartesian[F, E] do return new Cartesian[F, E](cf, ce)
+end
+
+# An iterator over a `Cartesian`-product collection.
+class CartesianIterator[E,F]
+       super Iterator[Pair[E,F]]
+
+       # The associated Cartesian-product collection.
+       var collection: Cartesian[E,F]
+
+       # The iterator over the first collection of the Cartesian product.
+       # Will be used only once.
+       private var ice: Iterator[E] is noinit
+
+       # The iterator over the second collection of the Cartesian product.
+       # Will be used once for each element of the first collection.
+       private var icf: Iterator[F] is noinit
+
+       init do
+               # Initialize each iterator
+               ice = collection.ce.iterator
+               icf = collection.cf.iterator
+       end
+
+       redef fun is_ok do return ice.is_ok and icf.is_ok
+
+       redef fun item do
+               # We lazily create the pair here
+               var res = item_cache
+               if res == null then
+                       res = new Pair[E,F](ice.item, icf.item)
+                       item_cache = res
+               end
+               return res
+       end
+
+       # Cached pair created by `item` and cleared by `next`.
+       private var item_cache: nullable Pair[E,F] = null
+
+       redef fun next do
+               # Next item in the second iterator
+               icf.next
+               if not icf.is_ok then
+                       # If it is over, then reset it and advance the first iterator
+                       icf = collection.cf.iterator
+                       ice.next
+               end
+               # Reset the cache
+               item_cache = null
+       end
+
+       # First member of `item`.
+       #
+       # This method shortcut the allocation of a `Pair`, thus should be more time and memory efficient.
+       fun item_e: E do return ice.item
+
+       # Second member of `item`.
+       #
+       # This method shortcut the allocation of a `Pair`, thus should be more time and memory efficient.
+       fun item_f: E do return icf.item
+end
diff --git a/lib/combinations.nit b/lib/combinations.nit
new file mode 100644 (file)
index 0000000..4076c03
--- /dev/null
@@ -0,0 +1,439 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# Cartesian products, combinations and permutation on collections.
+#
+# This module offers memory-efficient views on combinatoric collections.
+# Methods of the views create objects only when needed.
+# Moreover, produced objects during iterations are free to be collected and
+# their memory reused.
+#
+# This enable these views and method to works with very combinatoric large collections.
+#
+# When small combinatoric views need to be kept in memory (for fast access by example).
+# The `Collection::to_a` method and other related factories can be used to transform
+# the combinatoric views into extensive collections,
+module combinations
+
+redef class Collection[E]
+       # Cartesian product, over `r` times `self`.
+       #
+       # See `CartesianCollection` for details.
+       #
+       # FIXME: Cannot be used if RTA is enabled. So `niti` or `--erasure` only.
+       fun product(r: Int): Collection[SequenceRead[E]]
+       do
+               return new CartesianCollection[E]([self]*r)
+       end
+
+       # All `r`-length permutations on self (all possible ordering) without repeated elements.
+       #
+       # See `CartesianCollection` for details.
+       #
+       # FIXME: Cannot be used if RTA is enabled. So `niti` or `--erasure` only.
+       fun permutations(r: Int): Collection[SequenceRead[E]]
+       do
+               var res = new CombinationCollection[E](self, r)
+               res.are_sorted = false
+               res.are_unique = true
+               return res
+       end
+
+       # All `r`-length combinations on self (in same order) without repeated elements.
+       #
+       # See `CartesianCollection` for details.
+       #
+       # FIXME: Cannot be used if RTA is enabled. So `niti` or `--erasure` only.
+       fun combinations(r: Int): Collection[SequenceRead[E]]
+       do
+               var res = new CombinationCollection[E](self, r)
+               res.are_sorted = true
+               res.are_unique = true
+               return res
+       end
+
+       # All `r`-length combination on self (in same order) with repeated elements.
+       #
+       # See `CartesianCollection` for details.
+       #
+       # FIXME: Cannot be used if RTA is enabled. So `niti` or `--erasure` only.
+       fun combinations_with_replacement(r: Int): Collection[SequenceRead[E]]
+       do
+               var res = new CombinationCollection[E](self, r)
+               res.are_sorted = true
+               res.are_unique = false
+               return res
+       end
+end
+
+# A view of a Cartesian-product collection over homogeneous collections.
+#
+# Therefore, this view *generates* all the sequences of elements constructed by associating
+# en element for each one of the original collections.
+#
+# It is equivalent to doing nesting `for` for each collection.
+#
+# ~~~~
+# var xs = [1, 2, 3]
+# var ys = [8, 9]
+# var xys = new CartesianCollection[Int]([xs, ys])
+# assert xys.length == 6
+# assert xys.to_a == [[1,8], [1,9], [2,8], [2,9], [3,8], [3,9]]
+# ~~~~
+#
+# The pattern of the generate sequences produces a lexicographical order.
+#
+# Because it is a generator, it is memory-efficient and the sequences are created only when needed.
+#
+# Note: because it is a view, changes on the base collections are reflected on the view.
+#
+# ~~~~
+# assert xs.pop == 3
+# assert ys.pop == 9
+# assert xys.to_a == [[1,8], [2,8]]
+# ~~~~
+class CartesianCollection[E]
+       super Collection[SequenceRead[E]]
+
+       # The base collections used to generate the sequences.
+       var collections: SequenceRead[Collection[E]]
+
+       redef fun length
+       do
+               var res = 1
+               for c in collections do res = res * c.length
+               return res
+       end
+
+       redef fun iterator do return new CartesianIterator[E](self)
+end
+
+private class CartesianIterator[E]
+       super Iterator[SequenceRead[E]]
+       var collection: CartesianCollection[E]
+
+       # The array of iterations that will be increased in the lexicographic order.
+       private var iterators = new Array[Iterator[E]]
+
+       init
+       do
+               for c in collection.collections do
+                       var it = c.iterator
+                       iterators.add it
+                       if not it.is_ok then is_ok = false
+               end
+       end
+
+       redef var is_ok = true
+
+       redef fun item
+       do
+               var len = iterators.length
+               var res = new Array[E].with_capacity(len)
+               for i in [0..len[ do
+                       var it = iterators[i]
+                       res.add(it.item)
+               end
+               return res
+       end
+
+       redef fun next
+       do
+               var rank = iterators.length - 1
+
+               # Odometer-like increment starting from the last iterator
+               loop
+                       var it = iterators[rank]
+                       it.next
+                       if it.is_ok then return
+
+                       # The iterator if over
+                       if rank == 0 then
+                               # It it is the first, then the whole thing is over
+                               is_ok = false
+                               return
+                       end
+
+                       # If not, restart the iterator and increment the previous one
+                       # (like a carry)
+                       iterators[rank] = collection.collections[rank].iterator
+                       rank -= 1
+               end
+       end
+end
+
+# A view of some combinations over a base collections.
+#
+# This view *generates* some combinations and permutations on a collection.
+#
+# By default, the generated sequences are combinations:
+#
+#   * each sequence has a length of `repeat`
+#   * elements are in sorted order (see `are_sorted` for details)
+#   * no repeated element (see `are_unique` for details)
+#
+# ~~~~
+# var xs = [1, 2, 3]
+# var cxs = new CombinationCollection[Int](xs, 2)
+# assert cxs.length == 3
+# assert cxs.to_a == [[1,2], [1,3], [2,3]]
+# ~~~~
+#
+# Other kind of combinations can be generated by tweaking the attributes `are_sorted` and `are_unique`.
+#
+# * for permutation:
+#
+# ~~~~
+# cxs.are_sorted = false
+# cxs.are_unique = true
+# assert cxs.length == 6
+# assert cxs.to_a == [[1,2], [1,3], [2,1], [2,3], [3,1], [3,2]]
+# ~~~~
+#
+# * for combinations with replacement:
+#
+# ~~~~
+# cxs.are_sorted = true
+# cxs.are_unique = false
+# assert cxs.length == 6
+# assert cxs.to_a == [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]
+# ~~~~
+#
+# * for product:
+#
+# ~~~~
+# cxs.are_sorted = false
+# cxs.are_unique = false
+# assert cxs.length == 9
+# assert cxs.to_a == [[1,1], [1,2], [1,3], [2,1], [2,2], [2,3], [3,1], [3,2], [3,3]]
+# ~~~~
+#
+# However, in the last case, a faster alternative is to use `CartesianCollection`:
+#
+# ~~~~
+# var cp = new CartesianCollection[Int]([xs] * 2)
+# assert cp.to_a == cxs.to_a
+# ~~~~
+#
+# As seen in the examples, the patterns of the generated sequences produce a lexicographical order.
+#
+# Because it is a generator, it is memory-efficient and the sequences are created only when needed.
+#
+# Note: because it is a view, changes on the base collection are reflected on the view.
+#
+# ~~~~
+# assert xs.pop == 3
+# cxs.are_sorted = true
+# cxs.are_unique = true
+# assert cxs.to_a == [[1,2]]
+# ~~~~
+class CombinationCollection[E]
+       super Collection[SequenceRead[E]]
+
+       # The base collection used to generate the sequences.
+       var collection: Collection[E]
+
+       # The maximum length of each generated sequence.
+       var repeat: Int
+
+       init
+       do
+               assert repeat >= 0
+       end
+
+       # Are the elements in the generated sequences sorted?
+       # Default `true`.
+       #
+       # When `true`, the original order is preserved.
+       #
+       # Elements are compared by their order in the base collection,
+       # not by their intrinsic value or comparability.
+       #
+       # ~~~~
+       # var xs = [1, 1, 2]
+       # var cxs = new CombinationCollection[Int](xs, 2)
+       # cxs.are_sorted = true
+       # assert cxs.to_a == [[1,1], [1,2], [1, 2]]
+       # cxs.are_sorted = false
+       # assert cxs.to_a == [[1,1], [1,2], [1, 1], [1, 2], [2, 1], [2, 1]]
+       # ~~~~
+       var are_sorted = true is writable
+
+       # Are the element in the generated sequence unique?
+       # Default `true`.
+       #
+       # When `true`, an element cannot be reused in the same sequence (no replacement).
+       #
+       # Elements are distinguished by their order in the base collection,
+       # not by their intrinsic value or equality.
+       #
+       # ~~~~
+       # var xs = [1, 1, 2]
+       # var cxs = new CombinationCollection[Int](xs, 2)
+       # cxs.are_unique = true
+       # assert cxs.to_a == [[1,1], [1,2], [1, 2]]
+       # cxs.are_unique = false
+       # assert cxs.to_a == [[1,1], [1,1], [1,2], [1,1], [1,2], [2,2]]
+       # ~~~~
+       var are_unique = true is writable
+
+       redef fun length
+       do
+               var n = collection.length
+               if are_unique then
+                       if repeat > n then
+                               return 0
+                       end
+                       if are_sorted then
+                               return n.factorial / repeat.factorial
+                       else
+                               return n.factorial / (n-repeat).factorial
+                       end
+               else
+                       if are_sorted then
+                               return (n+repeat-1).factorial / repeat.factorial / (n-1).factorial
+                       else
+                               return n ** repeat
+                       end
+               end
+       end
+
+       redef fun iterator do
+               return new CombinationIterator[E](self)
+       end
+end
+
+private class CombinationIterator[E]
+       super Iterator[SequenceRead[E]]
+       var product: CombinationCollection[E]
+
+       private var iterators = new Array[Iterator[E]]
+       private var indices = new Array[Int]
+
+       var are_sorted: Bool is noinit
+       var are_unique: Bool is noinit
+
+       init
+       do
+               are_sorted = product.are_sorted
+               are_unique = product.are_unique
+
+               for rank in [0..product.repeat[ do
+                       reset_iterator(rank)
+               end
+       end
+
+       redef var is_ok = true
+
+       redef fun item
+       do
+               var len = product.repeat
+               var res = new Array[E].with_capacity(len)
+               for i in [0..len[ do
+                       var it = iterators[i]
+                       res.add(it.item)
+               end
+               return res
+       end
+
+       redef fun next
+       do
+               var rank = product.repeat - 1
+
+               loop
+                       var it = iterators[rank]
+
+                       if are_unique and not are_sorted then
+                               var idx = indices[rank] + 1
+                               it.next
+                               var adv = next_free(rank, idx)
+                               for i in [idx..adv[ do it.next
+                               indices[rank] = adv
+                       else
+                               it.next
+                               indices[rank] += 1
+                       end
+
+                       if it.is_ok then break
+                       if rank == 0 then
+                               is_ok = false
+                               return
+                       end
+                       rank -= 1
+               end
+
+               for r in [rank+1..product.repeat[ do
+                       reset_iterator(r)
+               end
+       end
+
+       private fun next_free(rank: Int, start: Int): Int
+       do
+               loop
+                       for i in [0..rank[ do
+                               if indices[i] == start then
+                                       start += 1
+                                       continue label
+                               end
+                       end
+                       break label
+               end label
+               return start
+       end
+
+       private fun reset_iterator(rank: Int): Iterator[E]
+       do
+               var it = product.collection.iterator
+               iterators[rank] = it
+               var skip = 0
+
+               if (not are_sorted and not are_unique) or rank == 0 then
+                       # DO NOTHING
+               else if are_sorted and are_unique then
+                       skip = indices[rank-1] + 1
+               else if are_sorted then
+                       skip = indices[rank-1]
+               else
+                       skip = next_free(rank, 0)
+               end
+
+               for i in [0..skip[ do it.next
+               indices[rank] = skip
+               if not it.is_ok then is_ok = false
+               return it
+       end
+
+       fun need_skip: Bool
+       do
+               if not are_sorted and not are_unique then
+                       return false
+               else if are_sorted and are_unique then
+                       var max = -1
+                       for i in indices do
+                               if i <= max then return true
+                               max = i
+                       end
+                       return false
+               else if are_sorted then
+                       var max = -1
+                       for i in indices do
+                               if i < max then return true
+                               max = i
+                       end
+                       return false
+               else
+                       # are_unique
+                       for i in indices do
+                               if indices.count(i) > 1 then return true
+                       end
+                       return false
+               end
+       end
+end
index de06850..7d710bd 100644 (file)
@@ -394,6 +394,26 @@ class Array[E]
                res.append(other)
                return res
        end
+
+       # Repetition of arrays.
+       #
+       # returns a new array built by concatenating `self` `repeat` times.
+       #
+       #    var a = [1,2,3]
+       #    assert (a * 0).is_empty
+       #    assert a * 1  ==  [1,2,3]
+       #    assert a * 2  ==  [1,2,3,1,2,3]
+       #    assert (a * 10).length  ==  30
+       fun *(repeat: Int): Array[E]
+       do
+               assert repeat >= 0
+               var res = new Array[E].with_capacity(length * repeat)
+               while repeat > 0 do
+                       res.add_all(self)
+                       repeat -= 1
+               end
+               return res
+       end
 end
 
 # An `Iterator` on `AbstractArray`
index a2e0966..5a980aa 100644 (file)
@@ -83,6 +83,34 @@ redef class Int
        #
        #    assert not 13.is_even
        fun is_odd: Bool do return not is_even
+
+       # Returns the `self` raised to the power of `e`.
+       #
+       #    assert 2 ** 3 == 8
+       fun **(e: Int): Int
+       do
+               return self.to_f.pow(e.to_f).to_i
+       end
+
+       # The factorial of `self` (aka `self!`)
+       #
+       # Returns `1 * 2 * 3 * ... * self-1 * self`
+       #
+       #    assert 0.factorial == 1  # by convention for an empty product
+       #    assert 1.factorial == 1
+       #    assert 4.factorial == 24
+       #    assert 9.factorial == 362880
+       fun factorial: Int
+       do
+               assert self >= 0
+               var res = 1
+               var n = self
+               while n > 0 do
+                       res = res * n
+                       n -= 1
+               end
+               return res
+       end
 end
 
 redef class Float
index 6a36b2b..b6cb920 100644 (file)
@@ -120,21 +120,21 @@ redef class AMethPropdef
                                mtype.compile_extern_type(v, ccu)
 
                                # has callbacks already been compiled? (this may very well happen with global compilation)
-                               if mmodule.check_callback_compilation(mtype) then mtype.compile_extern_helper_functions(v, ccu)
+                               mtype.compile_extern_helper_functions(v, ccu, mmodule.check_callback_compilation(mtype))
                        end
                end
 
                # compile callbacks
-               for cb in foreign_callbacks.callbacks do if mainmodule.check_callback_compilation(cb) then
-                       cb.compile_extern_callback(v, ccu)
+               for cb in foreign_callbacks.callbacks do
+                       cb.compile_extern_callback(v, ccu, mainmodule.check_callback_compilation(cb))
                end
 
-               for cb in foreign_callbacks.supers do if mainmodule.check_callback_compilation(cb) then
-                       cb.compile_extern_callback(v, ccu)
+               for cb in foreign_callbacks.supers do
+                       cb.compile_extern_callback(v, ccu, mainmodule.check_callback_compilation(cb))
                end
 
-               for cb in foreign_callbacks.casts do if mainmodule.check_callback_compilation(cb) then
-                       cb.compile_extern_callbacks(v, ccu)
+               for cb in foreign_callbacks.casts do
+                       cb.compile_extern_callbacks(v, ccu, mainmodule.check_callback_compilation(cb))
                end
 
                # manage nitni callback set
@@ -312,7 +312,7 @@ redef class MType
                ccu.header_c_types.add("#endif\n")
        end
 
-       private fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
+       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
@@ -330,7 +330,7 @@ redef class MType
 end
 
 redef class MNullableType
-       redef fun compile_extern_helper_functions(v, ccu)
+       redef fun compile_extern_helper_functions(v, ccu, compile_implementation_too)
        do
                super
 
@@ -344,6 +344,8 @@ redef class MNullableType
                # In nitni files, #define friendly as extern
                ccu.header_decl.add("#define {base_cname} {full_cname}\n")
 
+               if not compile_implementation_too then return
+
                # FIXME: This is ugly an broke the separate compilation principle
                # The real function MUST be compiled only once, #define pragma only protect the compiler, not the loader
                # However, I am not sure of the right approach here (eg. week refs are ugly)
@@ -364,7 +366,7 @@ redef class MNullableType
 end
 
 redef class MExplicitCall
-       private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
+       private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit, compile_implementation_too: Bool)
        do
                var mproperty = mproperty
                assert mproperty isa MMethod
@@ -373,6 +375,8 @@ redef class MExplicitCall
                var full_friendly_csignature = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context)
                ccu.header_decl.add("extern {full_friendly_csignature};\n")
 
+               if not compile_implementation_too then return
+
                # Internally, implement internal function
                var nitni_visitor = v.compiler.new_visitor
                nitni_visitor.frame = v.frame
@@ -424,7 +428,7 @@ redef class MExplicitCall
 end
 
 redef class MExplicitSuper
-       private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
+       private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit, compile_implementation_too: Bool)
        do
                var mproperty = from.mproperty
                assert mproperty isa MMethod
@@ -439,6 +443,8 @@ redef class MExplicitSuper
                var internal_cname = mproperty.build_cname(mclass_type, v.compiler.mainmodule, "___super", long_signature)
                ccu.header_decl.add("#define {friendly_cname} {internal_cname}\n")
 
+               if not compile_implementation_too then return
+
                # Internally, implement internal function
                var nitni_visitor = v.compiler.new_visitor
                nitni_visitor.frame = v.frame
@@ -477,7 +483,7 @@ redef class MExplicitSuper
 end
 
 redef class MExplicitCast
-       private fun compile_extern_callbacks(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
+       private fun compile_extern_callbacks(v: AbstractCompilerVisitor, ccu: CCompilationUnit, compile_implementation_too: Bool)
        do
                var from = from
                var to = to
@@ -493,22 +499,23 @@ redef class MExplicitCast
                # In nitni files, #define friendly as extern
                ccu.header_decl.add("#define {check_cname} {v.compiler.mainmodule.name}___{check_cname}\n")
 
-               # Internally, implement internal function
-               var nitni_visitor = v.compiler.new_visitor
-               nitni_visitor.frame = v.frame
+               if compile_implementation_too then
+                       # Internally, implement internal function
+                       var nitni_visitor = v.compiler.new_visitor
+                       nitni_visitor.frame = v.frame
 
-               var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
+                       var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
 
-               nitni_visitor.add_decl("/* nitni check for {from} to {to} */")
-               nitni_visitor.add_decl("{full_internal_csignature} \{")
+                       nitni_visitor.add_decl("/* nitni check for {from} to {to} */")
+                       nitni_visitor.add_decl("{full_internal_csignature} \{")
 
-               #var from_var = new RuntimeVariable("from->value", from, from)
-               var from_var = nitni_visitor.var_from_c("from", from)
-               from_var = nitni_visitor.box_extern(from_var, from)
-               var recv_var = nitni_visitor.type_test(from_var, to, "FFI isa")
-               nitni_visitor.add("return {recv_var};")
+                       var from_var = nitni_visitor.var_from_c("from", from)
+                       from_var = nitni_visitor.box_extern(from_var, from)
+                       var recv_var = nitni_visitor.type_test(from_var, to, "FFI isa")
+                       nitni_visitor.add("return {recv_var};")
 
-               nitni_visitor.add("\}")
+                       nitni_visitor.add("\}")
+               end
 
                # special checks
                if from == to.as_nullable then
@@ -527,30 +534,32 @@ redef class MExplicitCast
                # In nitni files, #define friendly as extern
                ccu.header_decl.add("#define {cast_cname} {v.compiler.mainmodule.name}___{cast_cname}\n")
 
-               # Internally, implement internal function
-               nitni_visitor = v.compiler.new_visitor
-               nitni_visitor.frame = v.frame
+               if compile_implementation_too then
+                       # Internally, implement internal function
+                       var nitni_visitor = v.compiler.new_visitor
+                       nitni_visitor.frame = v.frame
 
-               full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
-               nitni_visitor.add_decl("/* nitni cast for {from} to {to} */")
-               nitni_visitor.add_decl("{full_internal_csignature} \{")
+                       var full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
+                       nitni_visitor.add_decl("/* nitni cast for {from} to {to} */")
+                       nitni_visitor.add_decl("{full_internal_csignature} \{")
 
-               from_var = nitni_visitor.var_from_c("from", from)
-               from_var = nitni_visitor.box_extern(from_var, from)
+                       var from_var = nitni_visitor.var_from_c("from", from)
+                       from_var = nitni_visitor.box_extern(from_var, from)
 
-               ## test type
-               var check = nitni_visitor.type_test(from_var, to, "FFI cast")
-               nitni_visitor.add("if (!{check}) \{")
-               nitni_visitor.add_abort("FFI cast failed")
-               nitni_visitor.add("\}")
+                       ## test type
+                       var check = nitni_visitor.type_test(from_var, to, "FFI cast")
+                       nitni_visitor.add("if (!{check}) \{")
+                       nitni_visitor.add_abort("FFI cast failed")
+                       nitni_visitor.add("\}")
 
-               ## internal cast
-               recv_var = nitni_visitor.autobox(from_var, to)
-               recv_var = nitni_visitor.unbox_extern(recv_var, to)
+                       ## internal cast
+                       var recv_var = nitni_visitor.autobox(from_var, to)
+                       recv_var = nitni_visitor.unbox_extern(recv_var, to)
 
-               nitni_visitor.ret_to_c(recv_var, to)
+                       nitni_visitor.ret_to_c(recv_var, to)
 
-               nitni_visitor.add("\}")
+                       nitni_visitor.add("\}")
+               end
 
                # special casts
                if from.as_nullable == to then
diff --git a/tests/sav/test_ffi_c_duplicated_callback_a.res b/tests/sav/test_ffi_c_duplicated_callback_a.res
new file mode 100644 (file)
index 0000000..479d9d1
--- /dev/null
@@ -0,0 +1 @@
+Hello from `a`.
diff --git a/tests/sav/test_ffi_c_duplicated_callback_b.res b/tests/sav/test_ffi_c_duplicated_callback_b.res
new file mode 100644 (file)
index 0000000..8c51d75
--- /dev/null
@@ -0,0 +1,2 @@
+Hello from `a`.
+Hello from `b`.
diff --git a/tests/sav/test_ffi_cpp_duplicated_callback_a.res b/tests/sav/test_ffi_cpp_duplicated_callback_a.res
new file mode 100644 (file)
index 0000000..479d9d1
--- /dev/null
@@ -0,0 +1 @@
+Hello from `a`.
diff --git a/tests/sav/test_ffi_cpp_duplicated_callback_b.res b/tests/sav/test_ffi_cpp_duplicated_callback_b.res
new file mode 100644 (file)
index 0000000..8c51d75
--- /dev/null
@@ -0,0 +1,2 @@
+Hello from `a`.
+Hello from `b`.
index cfbc1fb..bf79e4b 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:769)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:789)
 NativeString
 N
 Nit
diff --git a/tests/test_ffi_c_duplicated_callback_a.nit b/tests/test_ffi_c_duplicated_callback_a.nit
new file mode 100644 (file)
index 0000000..3347747
--- /dev/null
@@ -0,0 +1,23 @@
+# 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.
+
+`{
+       #include <stdio.h>
+`}
+
+fun print_a(str: String) import String.to_cstring `{
+       puts(String_to_cstring(str));
+`}
+
+print_a "Hello from `a`."
diff --git a/tests/test_ffi_c_duplicated_callback_b.nit b/tests/test_ffi_c_duplicated_callback_b.nit
new file mode 100644 (file)
index 0000000..d55db5e
--- /dev/null
@@ -0,0 +1,26 @@
+# 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 test_ffi_c_duplicated_callback_a
+
+`{
+       #include <stdio.h>
+`}
+
+fun print_b(str: String) import String.to_cstring `{
+       puts(String_to_cstring(str));
+`}
+
+print_a "Hello from `a`."
+print_b "Hello from `b`."
diff --git a/tests/test_ffi_cpp_duplicated_callback_a.nit b/tests/test_ffi_cpp_duplicated_callback_a.nit
new file mode 100644 (file)
index 0000000..76c1172
--- /dev/null
@@ -0,0 +1,23 @@
+# 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.
+
+in "C++ Header" `{
+       #include <stdio.h>
+`}
+
+fun print_a(str: String) import String.to_cstring in "C++" `{
+       puts(String_to_cstring(str));
+`}
+
+print_a "Hello from `a`."
diff --git a/tests/test_ffi_cpp_duplicated_callback_b.nit b/tests/test_ffi_cpp_duplicated_callback_b.nit
new file mode 100644 (file)
index 0000000..01eb25c
--- /dev/null
@@ -0,0 +1,26 @@
+# 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 test_ffi_cpp_duplicated_callback_a
+
+in "C++ header" `{
+       #include <stdio.h>
+`}
+
+fun print_b(str: String) import String.to_cstring in "C++" `{
+       puts(String_to_cstring(str));
+`}
+
+print_a "Hello from `a`."
+print_b "Hello from `b`."