Merge: Java FFI: add GC pinning support
authorJean Privat <jean@pryen.org>
Mon, 9 Nov 2015 15:39:14 +0000 (10:39 -0500)
committerJean Privat <jean@pryen.org>
Mon, 9 Nov 2015 15:39:14 +0000 (10:39 -0500)
Adds a missing features to the Java FFI: pinning references to Nit objects from Java. Both services, `..._incr_ref` and `..._decr_ref`, are "simple" redirections from Java to the nitni service in C through the JNI.

Pull-Request: #1820
Reviewed-by: Jean Privat <jean@pryen.org>

84 files changed:
clib/gc_chooser.c
lib/core/package.ini
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/core/text/ropes.nit
lib/json/static.nit
lib/libevent.nit
lib/nitcorn/README.md
lib/nitcorn/examples/Makefile
lib/nitcorn/examples/src/simple_file_server.nit [moved from lib/nitcorn/examples/src/file_server_on_port_80.nit with 84% similarity]
lib/nitcorn/file_server.nit
lib/nitcorn/http_response.nit
lib/nitcorn/package.ini
lib/nitcorn/reactor.nit
src/compiler/abstract_compiler.nit
src/compiler/separate_compiler.nit
src/loader.nit
src/modelbuilder.nit
src/modelize/modelize_class.nit
src/nit.nit
src/nitcatalog.nit
src/nitvm.nit
src/platform/android.nit
src/semantize/typing.nit
src/toolcontext.nit
tests/error_array_ambig.nit
tests/error_attr_2def.nit
tests/error_attr_assign.nit
tests/error_cons_arity.nit
tests/error_cons_arity2.nit
tests/error_constraint.nit
tests/error_decl_type_var.nit
tests/error_for_coll.nit
tests/error_formal.nit
tests/error_formal_name.nit
tests/error_fun_ret.nit
tests/error_fun_ret2.nit
tests/error_fun_ret3.nit
tests/error_fun_ret4.nit
tests/error_fun_ret5.nit
tests/error_gen_f_inh_clash.nit
tests/error_if_bool.nit
tests/error_inh_clash.nit
tests/error_inh_clash2.nit
tests/error_inh_clash3.nit
tests/error_inh_clash4.nit
tests/error_inh_loop.nit
tests/error_kern_attr_any.nit
tests/error_kern_attr_int.nit
tests/error_left_bool.nit
tests/error_loop_bool_while.nit
tests/error_meth_2def.nit
tests/error_meth_2def2.nit
tests/error_redef3.nit
tests/error_redef4.nit
tests/error_redef_class.nit
tests/error_ref_attr.nit
tests/error_ref_fun.nit
tests/error_ref_param.nit
tests/error_ref_proc.nit
tests/error_ref_ret.nit
tests/error_ret_fun.nit
tests/error_ret_type.nit
tests/error_right_bool.nit
tests/error_signature.nit
tests/error_spe_attr.nit
tests/error_spe_fun.nit
tests/error_spe_param.nit
tests/error_spe_param2.nit
tests/error_spe_proc.nit
tests/error_spe_ret.nit
tests/error_super_none.nit
tests/error_type_not_ok.nit
tests/error_type_not_ok2.nit
tests/error_type_not_ok3.nit
tests/error_type_not_ok4.nit
tests/error_unk_class.nit
tests/sav/file_server_on_port_80.res [deleted file]
tests/sav/fixme/neo_doxygen_dump_args4.res [moved from tests/sav/neo_doxygen_dump_args4.res with 98% similarity]
tests/sav/fixme/neo_doxygen_dump_args5.res [moved from tests/sav/neo_doxygen_dump_args5.res with 98% similarity]
tests/sav/simple_file_server.res [new file with mode: 0644]
tests/sav/test_json_static.res
tests/sav/test_json_unicode.res [new file with mode: 0644]
tests/test_json_unicode.nit [new file with mode: 0644]

index 497cc7e..d3854c8 100644 (file)
@@ -18,6 +18,9 @@
 #ifdef ANDROID
        #include <android/log.h>
        #define PRINT_ERROR(...) ((void)__android_log_print(ANDROID_LOG_WARN, "nit", __VA_ARGS__))
+
+       // FIXME bring back when the GC is fixed in Android
+       #undef WITH_LIBGC
 #else
        #define PRINT_ERROR(...) ((void)fprintf(stderr, __VA_ARGS__))
 #endif
index 9b4db38..7b938af 100644 (file)
@@ -3,6 +3,7 @@ name=core
 tags=lib
 maintainer=Jean Privat <jean@pryen.org>
 license=Apache-2.0
+more_contributors=Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>, Romain Chanoir <romain.chanoir@viacesi.fr>, Christophe Gigax <christophe.gigax@viacesi.fr>, Frédéric Vachon <fredvac@gmail.com>, Jean-Sebastien Gelinas <calestar@gmail.com>, Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>, Johan Kayser <johan.kayser@viacesi.fr>, Johann Dubois <johann.dubois@outlook.com>, Julien Pagès <julien.projet@gmail.com>
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/lib/core
 git=https://github.com/nitlang/nit.git
index baf8ae7..64c22c0 100644 (file)
@@ -718,6 +718,38 @@ abstract class Text
                return res.to_s
        end
 
+       # Returns `self` with all characters escaped with their UTF-16 representation
+       #
+       #     assert "Aèあ𐏓".escape_to_utf16 == "\\u0041\\u00e8\\u3042\\ud800\\udfd3"
+       fun escape_to_utf16: String do
+               var buf = new Buffer
+               for i in chars do buf.append i.escape_to_utf16
+               return buf.to_s
+       end
+
+       # Returns the Unicode char escaped by `self`
+       #
+       #     assert "\\u0041".from_utf16_escape == 'A'
+       #     assert "\\ud800\\udfd3".from_utf16_escape == '𐏓'
+       #     assert "\\u00e8".from_utf16_escape == 'è'
+       #     assert "\\u3042".from_utf16_escape == 'あ'
+       fun from_utf16_escape: Char do
+               var ln = length
+               if ln != 6 and ln != 12 then return 0xFFFD.code_point
+               var cphi = substring(2, 4).to_hex
+               if cphi < 0xD800 then return cphi.code_point
+               if cphi > 0xDFFF then return cphi.code_point
+               if cphi > 0xDBFF then return 0xFFFD.code_point
+               var cp = 0
+               cp += (cphi - 0xD800) << 10
+               var cplo = substring(8, 4).to_hex
+               if cplo < 0xDC00 then return 0xFFFD.code_point
+               if cplo > 0xDFFF then return 0xFFFD.code_point
+               cp += cplo - 0xDC00
+               cp += 0x10000
+               return cp.code_point
+       end
+
        # Encode `self` to percent (or URL) encoding
        #
        #     assert "aBc09-._~".to_percent_encoding == "aBc09-._~"
@@ -1587,6 +1619,46 @@ redef class Char
                return ns.to_s_with_length(ln)
        end
 
+       # Returns `self` escaped to UTF-16
+       #
+       # i.e. Represents `self`.`code_point` using UTF-16 codets escaped
+       # with a `\u`
+       #
+       #     assert 'A'.escape_to_utf16 == "\\u0041"
+       #     assert 'è'.escape_to_utf16 == "\\u00e8"
+       #     assert 'あ'.escape_to_utf16 == "\\u3042"
+       #     assert '𐏓'.escape_to_utf16 == "\\ud800\\udfd3"
+       fun escape_to_utf16: String do
+               var cp = code_point
+               var buf: Buffer
+               if cp < 0xD800 or (cp >= 0xE000 and cp <= 0xFFFF) then
+                       buf = new Buffer.with_cap(6)
+                       buf.append("\\u0000")
+                       var hx = cp.to_hex
+                       var outid = 5
+                       for i in hx.chars.reverse_iterator do
+                               buf[outid] = i
+                               outid -= 1
+                       end
+               else
+                       buf = new Buffer.with_cap(12)
+                       buf.append("\\u0000\\u0000")
+                       var lo = (((cp - 0x10000) & 0x3FF) + 0xDC00).to_hex
+                       var hi = ((((cp - 0x10000) & 0xFFC00) >> 10) + 0xD800).to_hex
+                       var out = 2
+                       for i in hi do
+                               buf[out] = i
+                               out += 1
+                       end
+                       out = 8
+                       for i in lo do
+                               buf[out] = i
+                               out += 1
+                       end
+               end
+               return buf.to_s
+       end
+
        private fun u8char_tos(r: NativeString, len: Int) `{
                r[len] = '\0';
                switch(len){
index c89b222..d58aafb 100644 (file)
@@ -878,18 +878,15 @@ class FlatBuffer
                assert count >= 0
                if from < 0 then from = 0
                if (from + count) > length then count = length - from
-               if count != 0 then
-                       var its = _items
-                       var bytefrom = its.char_to_byte_index(from)
-                       var byteto = its.char_to_byte_index(count + from - 1)
-                       byteto += its.char_at(byteto).u8char_len - 1
-                       var byte_length = byteto - bytefrom + 1
-                       var r_items = new NativeString(byte_length)
-                       its.copy_to(r_items, byte_length, bytefrom, 0)
-                       return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
-               else
-                       return new Buffer
-               end
+               if count <= 0 then return new Buffer
+               var its = _items
+               var bytefrom = its.char_to_byte_index(from)
+               var byteto = its.char_to_byte_index(count + from - 1)
+               byteto += its.char_at(byteto).u8char_len - 1
+               var byte_length = byteto - bytefrom + 1
+               var r_items = new NativeString(byte_length)
+               its.copy_to(r_items, byte_length, bytefrom, 0)
+               return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
        end
 
        redef fun reverse
index c164529..cd7064f 100644 (file)
@@ -463,14 +463,15 @@ class RopeBuffer
 
        redef fun add(c) do
                var rp = rpos
-               if rp >= buf_size then
+               var remsp = buf_size - rp
+               var cln = c.u8char_len
+               if cln > remsp then
                        dump_buffer
                        rp = 0
                end
-               # TODO: Fix when supporting UTF-8
-               ns[rp] = c.ascii
-               rp += 1
-               _bytelen += 1
+               ns.set_char_at(rp, c)
+               rp += cln
+               _bytelen += cln
                rpos = rp
        end
 
@@ -1211,7 +1212,7 @@ class RopeBufferByteReverseIterator
 
        redef fun next do
                index -= 1
-               if pns > 0 then
+               if pns >= 0 then
                        pns -= 1
                else
                        sit.next
index 966f2c9..6f2a9e4 100644 (file)
@@ -49,7 +49,7 @@ interface Jsonable
        protected fun to_json_by_append: String do
                var buffer = new RopeBuffer
                append_json(buffer)
-               return buffer.write_to_string
+               return buffer.to_s
        end
 
        # Append the JSON representation of `self` to the specified buffer.
@@ -80,7 +80,7 @@ interface Jsonable
                var res = new FlatBuffer
                pretty_json_visit(res, 0)
                res.add '\n'
-               return res.write_to_string
+               return res.to_s
        end
 
        private fun pretty_json_visit(buffer: FlatBuffer, indent: Int) is abstract
@@ -91,7 +91,7 @@ redef class Text
 
        redef fun append_json(buffer) do
                buffer.add '\"'
-               for i in [0..self.length[ do
+               for i in [0 .. self.length[ do
                        var char = self[i]
                        if char == '\\' then
                                buffer.append "\\\\"
@@ -99,7 +99,7 @@ redef class Text
                                buffer.append "\\\""
                        else if char == '\/' then
                                buffer.append "\\/"
-                       else if char < 16.code_point then
+                       else if char < ' ' then
                                if char == '\n' then
                                        buffer.append "\\n"
                                else if char == '\r' then
@@ -111,10 +111,8 @@ redef class Text
                                else if char == 0x08.code_point then
                                        buffer.append "\\b"
                                else
-                                       buffer.append "\\u000{char.code_point.to_hex}"
+                                       buffer.append char.escape_to_utf16
                                end
-                       else if char < ' ' then
-                               buffer.append "\\u00{char.code_point.to_hex}"
                        else
                                buffer.add char
                        end
@@ -427,7 +425,7 @@ end
 redef class Nstring
        # The represented string.
        private fun to_nit_string: String do
-               var res = new FlatBuffer
+               var res = new Buffer
                var i = 1
                while i < text.length - 1 do
                        var char = text[i]
@@ -445,21 +443,31 @@ redef class Nstring
                                else if char == 't' then
                                        char = '\t'
                                else if char == 'u' then
-                                       var code = text.substring(i + 1, 4).to_hex
-                                       # TODO UTF-16 escaping is not supported yet.
-                                       if code >= 128 then
-                                               char = '?'
-                                       else
-                                               char = code.code_point
+                                       var escape = new Buffer
+                                       escape.append "\\u"
+                                       var code = text.substring(i + 1, 4)
+                                       escape.append code
+                                       var hx = code.to_hex
+                                       if hx >= 0xD800 and hx <= 0xDFFF then
+                                               var lostr = text.substring(i + 7, 4)
+                                               if lostr.length < 4 then
+                                                       escape.clear
+                                                       escape.append "\\uFFFD"
+                                               else
+                                                       escape.append "\\u"
+                                                       escape.append lostr
+                                               end
+                                               i += 6
                                        end
                                        i += 4
+                                       char = escape.from_utf16_escape
                                end
                                # `"`, `/` or `\` => Keep `char` as-is.
                        end
                        res.add char
                        i += 1
                end
-               return res.write_to_string
+               return res.to_s
        end
 end
 
index 7794ed5..8fdc6f1 100644 (file)
@@ -162,19 +162,34 @@ class Connection
 
        redef fun write_byte(byte) do native_buffer_event.write_byte(byte)
 
+       redef fun write_bytes(bytes) do native_buffer_event.write(bytes.items, bytes.length)
+
        # Write a file to the connection
        #
-       # require: `path.file_exists`
+       # If `not path.file_exists`, the method returns.
        fun write_file(path: String)
        do
-               assert path.file_exists
-
                var file = new FileReader.open(path)
-               var output = native_buffer_event.output_buffer
-               var fd = file.fd
-               var length = file.file_stat.size
+               if file.last_error != null then
+                       var error = new IOError("Failed to open file at '{path}'")
+                       error.cause = file.last_error
+                       self.last_error = error
+                       file.close
+                       return
+               end
+
+               var stat = file.file_stat
+               if stat == null then
+                       last_error = new IOError("Failed to stat file at '{path}'")
+                       file.close
+                       return
+               end
 
-               output.add_file(fd, 0, length)
+               var err = native_buffer_event.output_buffer.add_file(file.fd, 0, stat.size)
+               if err then
+                       last_error = new IOError("Failed to add file at '{path}'")
+                       file.close
+               end
        end
 end
 
@@ -209,6 +224,12 @@ extern class NativeBufferEvent `{ struct bufferevent * `}
 
        # The input buffer associated to `self`
        fun input_buffer: InputNativeEvBuffer `{ return bufferevent_get_input(self); `}
+
+       # Read data from this buffer
+       fun read_buffer(buf: NativeEvBuffer): Int `{ return bufferevent_read_buffer(self, buf); `}
+
+       # Write data to this buffer
+       fun write_buffer(buf: NativeEvBuffer): Int `{ return bufferevent_write_buffer(self, buf); `}
 end
 
 # A single buffer
index c64b5cb..aa038c4 100644 (file)
@@ -2,7 +2,7 @@ The nitcorn Web server framework creates server-side Web apps in Nit
 
 # Examples
 
-Want to see `nitcorn` in action? Examples are available at ../../examples/nitcorn/src/.
+Want to see `nitcorn` in action? Examples are available at `examples/nitcorn/src/`.
 
 # Features and TODO list
 
@@ -29,7 +29,3 @@ Jean-Philippe Caissy, Guillaume Auger, Frederic Sevillano, Justin Michaud-Ouelle
 Stephan Michaud and Maxime Bélanger.
 
 It has been adapted to a library, and is currently maintained, by Alexis Laferrière.
-
-Other contributors:
-
-* Alexandre Terrasa
index a455325..9597941 100644 (file)
@@ -1,6 +1,6 @@
 all:
        mkdir -p bin/
-       ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
+       ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
 
 xymus.net:
        mkdir -p bin/
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Basic file server on port 80, usually requires `root` to execute
+# Basic file server on port 80 by default, may require `root` to execute
 #
 # To be safe, it is recommended to run this program with its own username:
 # `sudo file_server -u nitcorn:www`
-module file_server_on_port_80
+module simple_file_server
 
 import nitcorn
 import privileges
@@ -26,10 +26,10 @@ import privileges
 # Prepare options
 var opts = new OptionContext
 var opt_drop = new OptionUserAndGroup.for_dropping_privileges
-opt_drop.mandatory = true
+var opt_port = new OptionInt("Server port", 80, "--port", "-p")
 var opt_help = new OptionBool("Print this message", "--help", "-h")
-opts.add_option(opt_drop, opt_help)
-opts.parse(args)
+opts.add_option(opt_drop, opt_port, opt_help)
+opts.parse args
 
 # Check options errors and help
 if not opts.errors.is_empty or opt_help.value then
@@ -40,7 +40,7 @@ if not opts.errors.is_empty or opt_help.value then
 end
 
 # Serve everything with a standard FilesHandler
-var vh = new VirtualHost("localhost:80")
+var vh = new VirtualHost("localhost:{opt_port.value}")
 vh.routes.add new Route(null, new FileServer("www/hello_world/"))
 var factory = new HttpFactory.and_libevent
 factory.config.virtual_hosts.add vh
index d46d2a4..63ab11a 100644 (file)
@@ -145,8 +145,7 @@ class FileServer
                                        response.header["Content-Type"] = media_types["html"].as(not null)
                                else
                                        # It's a single file
-                                       var file = new FileReader.open(local_file)
-                                       response.body = file.read_all
+                                       response.files.add local_file
 
                                        var ext = local_file.file_extension
                                        if ext != null then
@@ -155,8 +154,6 @@ class FileServer
                                                        response.header["Content-Type"] = media_type
                                                else response.header["Content-Type"] = "application/octet-stream"
                                        end
-
-                                       file.close
                                end
 
                        else response = new HttpResponse(404)
index 523c051..1690864 100644 (file)
@@ -37,12 +37,36 @@ class HttpResponse
        # Body of this response
        var body = "" is writable
 
+       # Files appended after `body`
+       var files = new Array[String]
+
        # Finalize this response before sending it over HTTP
        fun finalize
        do
                # Set the content length if not already set
                if not header.keys.has("Content-Length") then
-                       header["Content-Length"] = body.bytelen.to_s
+                       # Size of the body
+                       var len = body.bytelen
+
+                       # Size of included files
+                       for path in files do
+                               # TODO handle these error cases elsewhere, an error here will result in an invalid response
+                               if not path.file_exists then
+                                       print_error "File does not exists at '{path}'"
+                                       continue
+                               end
+
+                               var stat = path.file_stat
+                               if stat == null then
+                                       print_error "Failed to stat file at '{path}'"
+                                       continue
+                               end
+
+                               len += stat.size
+                       end
+
+                       # Set header
+                       header["Content-Length"] = len.to_s
                end
 
                # Set server ID
index 0ea320a..94e1da2 100644 (file)
@@ -3,6 +3,7 @@ name=nitcorn
 tags=network,lib
 maintainer=Alexis Laferrière <alexis.laf@xymus.net>
 license=Apache-2.0
+more_contributors=Jean-Philippe Caissy <jean-philippe.caissy@shopify.com>, Guillaume Auger, Frederic Sevillano, Justin Michaud-Ouellette, Stephan Michaud, Maxime Bélanger
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/lib/nitcorn/
 git=https://github.com/nitlang/nit.git
index 8124f97..4b13677 100644 (file)
@@ -85,6 +85,8 @@ class HttpServer
 
                # Send back a response
                write response.to_s
+               for path in response.files do write_file path
+
                close
        end
 end
index 9584179..821ad31 100644 (file)
@@ -853,12 +853,14 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.add_decl("int main(int argc, char** argv) \{")
                end
 
+               v.add "#ifndef ANDROID"
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
                v.add("signal(SIGILL, sig_handler);")
                v.add("signal(SIGINT, sig_handler);")
                v.add("signal(SIGTERM, sig_handler);")
                v.add("signal(SIGSEGV, sig_handler);")
+               v.add "#endif"
                v.add("signal(SIGPIPE, SIG_IGN);")
 
                v.add("glob_argc = argc; glob_argv = argv;")
@@ -3894,11 +3896,7 @@ end
 # Here we load an process all modules passed on the command line
 var mmodules = modelbuilder.parse(arguments)
 
-if mmodules.is_empty then
-       toolcontext.check_errors
-       toolcontext.errors_info
-       if toolcontext.error_count > 0 then exit(1) else exit(0)
-end
+if mmodules.is_empty then toolcontext.quit
 
 modelbuilder.run_phases
 
index f6c483c..4c5fd25 100644 (file)
@@ -1053,7 +1053,7 @@ class SeparateCompiler
                v.add_abort("type null")
                v.add("\}")
                v.add("if({t}->table_size < 0) \{")
-               v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
+               v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
                v.add_abort("type dead")
                v.add("\}")
        end
index c80b6dc..b3485ed 100644 (file)
@@ -104,7 +104,7 @@ redef class ModelBuilder
 
                if toolcontext.opt_only_parse.value then
                        self.toolcontext.info("*** ONLY PARSE...", 1)
-                       exit(0)
+                       self.toolcontext.quit
                end
 
                return mmodules.to_a
@@ -199,7 +199,7 @@ redef class ModelBuilder
 
                if toolcontext.opt_only_parse.value then
                        self.toolcontext.info("*** ONLY PARSE...", 1)
-                       exit(0)
+                       self.toolcontext.quit
                end
 
                return mmodules.to_a
index 00e41b5..671fcd0 100644 (file)
@@ -100,7 +100,7 @@ redef class ModelBuilder
 
                if toolcontext.opt_only_metamodel.value then
                        self.toolcontext.info("*** ONLY METAMODEL", 1)
-                       exit(0)
+                       toolcontext.quit
                end
        end
 
index b3ab0bb..fa1fddb 100644 (file)
@@ -301,7 +301,6 @@ redef class ModelBuilder
        # REQUIRE: classes of imported modules are already build. (let `phase` do the job)
        private fun build_classes(nmodule: AModule)
        do
-               var errcount = toolcontext.error_count
                # Force building recursively
                if nmodule.build_classes_is_done then return
                nmodule.build_classes_is_done = true
@@ -312,8 +311,6 @@ redef class ModelBuilder
                        if nimp != null then build_classes(nimp)
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Create all classes
                # process AStdClassdef before so that non-AStdClassdef classes can be attached to existing ones, if any
                for nclassdef in nmodule.n_classdefs do
@@ -325,8 +322,6 @@ redef class ModelBuilder
                        self.build_a_mclass(nmodule, nclassdef)
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Create all classdefs
                for nclassdef in nmodule.n_classdefs do
                        if not nclassdef isa AStdClassdef then continue
@@ -337,29 +332,21 @@ redef class ModelBuilder
                        self.build_a_mclassdef(nmodule, nclassdef)
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Create inheritance on all classdefs
                for nclassdef in nmodule.n_classdefs do
                        self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Create the mclassdef hierarchy
                for mclassdef in mmodule.mclassdefs do
                        mclassdef.add_in_hierarchy
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Check inheritance
                for nclassdef in nmodule.n_classdefs do
                        self.check_supertypes(nmodule, nclassdef)
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Check unchecked ntypes
                for nclassdef in nmodule.n_classdefs do
                        if nclassdef isa AStdClassdef then
@@ -383,8 +370,6 @@ redef class ModelBuilder
                        end
                end
 
-               if errcount != toolcontext.error_count then return
-
                # Check clash of ancestors
                for nclassdef in nmodule.n_classdefs do
                        var mclassdef = nclassdef.mclassdef
@@ -405,13 +390,11 @@ redef class ModelBuilder
                        end
                end
 
-               if errcount != toolcontext.error_count then return
-
                # TODO: Check that the super-class is not intrusive
 
                # Check that the superclasses are not already known (by transitivity)
                for nclassdef in nmodule.n_classdefs do
-                       if not nclassdef isa AStdClassdef then continue
+                       if not nclassdef isa AStdClassdef or nclassdef.is_broken then continue
                        var mclassdef = nclassdef.mclassdef
                        if mclassdef == null then continue
 
index 196961d..9410b4d 100644 (file)
@@ -70,7 +70,7 @@ end
 
 modelbuilder.run_phases
 
-if toolcontext.opt_only_metamodel.value then exit(0)
+if toolcontext.opt_only_metamodel.value then toolcontext.quit
 
 var mainmodule = toolcontext.make_main_module(mmodules)
 
index 21847ee..33d1871 100644 (file)
@@ -447,6 +447,12 @@ class Catalog
                end
 
                var contributors = mpackage.contributors
+               var more_contributors = mpackage.metadata("package.more_contributors")
+               if more_contributors != null then
+                       for c in more_contributors.split(",") do
+                               contributors.add c.trim
+                       end
+               end
                if not contributors.is_empty then
                        res.add "<h3>Contributors</h3>\n<ul class=\"box\">"
                        for c in contributors do
index 088cd92..e81b20d 100644 (file)
@@ -45,7 +45,7 @@ var mmodules = modelbuilder.parse([progname])
 mmodules.add_all modelbuilder.parse(opt_mixins.value)
 modelbuilder.run_phases
 
-if toolcontext.opt_only_metamodel.value then exit(0)
+if toolcontext.opt_only_metamodel.value then toolcontext.quit
 
 var mainmodule: nullable MModule
 
index 1a6f458..f1d1476 100644 (file)
@@ -36,7 +36,7 @@ class AndroidPlatform
 
        redef fun name do return "android"
 
-       redef fun supports_libgc do return true
+       redef fun supports_libgc do return false
 
        redef fun supports_libunwind do return false
 
@@ -141,7 +141,7 @@ class AndroidToolchain
                ## Generate Application.mk
                dir = "{android_project_root}/jni/"
                """
-APP_ABI := armeabi armeabi-v7a x86 mips
+APP_ABI := armeabi armeabi-v7a x86
 APP_PLATFORM := android-{{{app_target_api}}}
 """.write_to_file "{dir}/Application.mk"
 
index 8a23a73..b4599d0 100644 (file)
@@ -117,6 +117,7 @@ private class TypeVisitor
                        #node.debug("Unsafe typing: expected {sup}, got {sub}")
                        return sup
                end
+               if sup isa MBottomType then return null # Skip error
                if sub.need_anchor then
                        var u = anchor_to(sub)
                        self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
@@ -1134,6 +1135,7 @@ redef class AForExpr
                        var mtype = v.visit_expr(g.n_expr)
                        if mtype == null then return
                        g.do_type_iterator(v, mtype)
+                       if g.is_broken then is_broken = true
                end
 
                v.visit_stmt(n_block)
index bdd6338..850781e 100644 (file)
@@ -169,6 +169,16 @@ class ToolContext
                return tags.has("all") or tags.has(tag)
        end
 
+       # Output all current stacked messages, total and exit the program
+       #
+       # If there is no error, exit with 0, else exit with 1.
+       fun quit
+       do
+               check_errors
+               errors_info
+               if error_count > 0 then exit(1) else exit(0)
+       end
+
        # Output all current stacked messages
        #
        # Return true if no errors occurred.
index 8ef8529..ef4beb6 100644 (file)
@@ -15,3 +15,4 @@
 # limitations under the License.
 
 var i = [4, '4']
+i.first.output
index e10ed39..d7d7122 100644 (file)
@@ -18,3 +18,6 @@ class A
        var toto: Int
        var toto: Object
 end
+
+var a = new A
+a.toto.output
index 173d9b5..8265b99 100644 (file)
@@ -21,3 +21,6 @@ class A
                _toto = 't'
        end
 end
+
+var a = new A(1)
+a.m
index 6c6ee71..3f1cede 100644 (file)
@@ -18,3 +18,6 @@ class C[E]
 end
 class C[E: Object, F: Object]
 end
+
+var c = new C
+c.output
index b1f4c5d..9dd4ccd 100644 (file)
@@ -19,3 +19,6 @@ end
 
 class A[E: Object]
 end
+
+var a = new A
+a.output
index 8ed9abf..61c1fa7 100644 (file)
@@ -19,3 +19,6 @@ class A
 end
 class A[E: Object]
 end
+
+var a = new A
+a.output
index 89d8bfe..0d31269 100644 (file)
@@ -15,3 +15,4 @@
 # limitations under the License.
 
 var i: Int = '4'
+i.output
index 42380aa..008d626 100644 (file)
@@ -15,4 +15,5 @@
 # limitations under the License.
 
 for i in 5 do
+       i.output
 end
index 13a16b5..a45a57a 100644 (file)
@@ -17,3 +17,6 @@
 class A[T]
        var k: T[Int]
 end
+
+var a = new A[Object]
+a.output
index be27718..9ebbb23 100644 (file)
@@ -17,3 +17,5 @@ import kernel
 class G[Foo]
        type Bar: Object
 end
+
+var g = new G[Object]
index 8744a8e..6cb6330 100644 (file)
@@ -17,3 +17,5 @@
 fun toto: Int
 do
 end
+
+toto.output
index 7f09bfc..d9beb18 100644 (file)
@@ -20,3 +20,5 @@ do
                return 0
        end
 end
+
+toto.output
index 0825cb5..bba3eaf 100644 (file)
@@ -21,3 +21,5 @@ do
                return 1
        end
 end
+
+toto.output
index 9d74ff0..bccb2e8 100644 (file)
@@ -21,3 +21,5 @@ do
                return 1
        end
 end
+
+toto.output
index b4173ec..065533e 100644 (file)
@@ -20,3 +20,5 @@ do
                return 1
        end
 end
+
+toto.output
index 6ea638d..12d8a0a 100644 (file)
@@ -20,3 +20,6 @@ class G3
        super G1
        super G2
 end
+
+var g = new G3
+g.output
index 17b0bf8..8380fa6 100644 (file)
@@ -15,4 +15,5 @@
 # limitations under the License.
 
 if 5 then
+       1.output
 end
index f9e31b5..bc04cd4 100644 (file)
@@ -18,3 +18,6 @@ class A
        super Array[Int]
        super Array[Char]
 end
+
+var a = new A
+a.output
index a66d081..2c1cb4a 100644 (file)
@@ -24,3 +24,6 @@ class C
        super A
        super B
 end
+
+var c = new C
+c.output
index f205c7a..8c720d8 100644 (file)
@@ -24,3 +24,6 @@ class C
        super A[Int]
        super B[Char]
 end
+
+var c = new C
+c.output
index 8f9928f..63d05e7 100644 (file)
@@ -16,3 +16,6 @@ class A[E, F]
        super Array[E]
        super List[F]
 end
+
+var a = new A[Int, Int]
+a.output
index 0e778f9..0f639a8 100644 (file)
@@ -27,3 +27,6 @@ end
 class C
        super A
 end
+
+var a = new A
+a.output
index e45209c..76d3763 100644 (file)
@@ -17,3 +17,5 @@
 redef class Object
        var toto: Bool
 end
+
+toto.output
index 5a0ea55..7056d6d 100644 (file)
@@ -17,3 +17,5 @@
 redef class Int
        var toto: Object
 end
+
+1.toto.output
index 0a2690a..dc9bcd8 100644 (file)
@@ -15,4 +15,5 @@
 # limitations under the License.
 
 if 5 and true then
+       1.output
 end
index d53eb60..74394dd 100644 (file)
@@ -15,4 +15,5 @@
 # limitations under the License.
 
 while 5 do
+       1.output
 end
index 140089c..7f41160 100644 (file)
@@ -18,3 +18,6 @@ class A
        fun toto(a: Int) do end
        fun toto(a: Char) do end
 end
+
+var a = new A
+a.toto(1)
index 8e10ea6..48c63a4 100644 (file)
@@ -18,3 +18,6 @@ class A
        fun toto(a: Int) do end
        fun toto(a: Int): Int do return 0 end
 end
+
+var a = new A
+a.toto(1).output
index b7075d6..eeeda92 100644 (file)
@@ -15,3 +15,5 @@
 class A end
 class A end
 
+var a = new A
+a.output
index cf2d3c9..8b2f3f1 100644 (file)
@@ -17,3 +17,5 @@ import error_redef4_base
 redef class A end
 redef class A end
 
+var a = new A
+a.output
index 87277d0..05363db 100644 (file)
@@ -15,3 +15,6 @@
 import kernel
 
 redef class Fail end
+
+var f = new Fail
+f.output
index 3022e6d..a0a2d27 100644 (file)
@@ -19,3 +19,6 @@ import module_simple
 redef class C
        redef var a: Int
 end
+
+var c = new C(new B)
+c.a.output
index 8267289..5a9a1e7 100644 (file)
@@ -19,3 +19,6 @@ import module_simple
 redef class C
        redef fun s do end
 end
+
+var c = new C(new B)
+c.s.output
index 4a9f644..e4f1b72 100644 (file)
@@ -19,3 +19,6 @@ import module_simple
 redef class C
        redef fun r(x: C) do end
 end
+
+var c = new C(new B)
+c.r
index 18f5452..02cd0f1 100644 (file)
@@ -19,3 +19,6 @@ import module_simple
 redef class C
        redef fun r(x: B): B do return self end
 end
+
+var c = new C(new B)
+c.r
index 1aedf5a..040de3e 100644 (file)
@@ -19,3 +19,6 @@ import module_simple
 redef class C
        redef fun s: Int do return 1 end
 end
+
+var c = new C(new B)
+c.s.output
index 3505137..9f737ac 100644 (file)
@@ -18,3 +18,5 @@ fun toto: Int
 do
        return
 end
+
+toto.output
index 41dc4b4..18f7808 100644 (file)
@@ -18,3 +18,5 @@ fun toto: Int
 do
        return '4'
 end
+
+(toto+1).output
index 188448b..7653555 100644 (file)
@@ -15,4 +15,5 @@
 # limitations under the License.
 
 if true or 6 then
+       1.output
 end
index 7da246a..47f3160 100644 (file)
@@ -27,3 +27,12 @@ class C
        super A
        redef fun foo: Int do return 3
 end
+
+var a = new A
+a.foo.output
+
+var b = new B
+b.foo
+
+var c = new C
+c.foo
index 5570921..00e43b6 100644 (file)
@@ -21,3 +21,6 @@ class B
        super A
        redef var a: Object = 2
 end
+
+var b = new B
+b.a.output
index 7a3ae87..669e139 100644 (file)
@@ -22,3 +22,6 @@ class B
        super A
 redef fun toto do end
 end
+
+var b = new B
+b.toto.output
index a3615ea..ac2b294 100644 (file)
@@ -23,3 +23,6 @@ class B
        super A
 redef fun toto(c: Object) do end
 end
+
+var b = new B
+b.toto(true)
index b0d63ac..35680b2 100644 (file)
@@ -23,3 +23,6 @@ class B
        super A
 redef fun toto(c: Char) do end
 end
+
+var b = new B
+b.toto(true)
index 0b23aef..17949f3 100644 (file)
@@ -20,5 +20,8 @@ end
 
 class B
        super A
-redef fun toto: Int do return 2end
+redef fun toto: Int do return 2 end
 end
+
+var b = new B
+b.toto.output
index 42c1848..e4330ff 100644 (file)
@@ -22,3 +22,6 @@ class B
        super A
 redef fun toto: Char do return 'a' end
 end
+
+var b = new B
+b.toto.output
index aeddbd9..605adfd 100644 (file)
@@ -21,3 +21,6 @@ class A
                super
        end
 end
+
+var a = new A
+a.foo
index cfe5302..10fd302 100644 (file)
@@ -21,3 +21,5 @@ class A
        super Fail
 end
 
+var a = new A
+a.output
index 4e49e03..b259d44 100644 (file)
@@ -21,3 +21,5 @@ class B
        super Array[Fail]
 end
 
+var b = new B
+b.output
index 7828cf6..77cab7c 100644 (file)
@@ -22,3 +22,8 @@ end
 
 class D[T: Array[Fail]]
 end
+
+var c = new C[Int]
+c.output
+var d = new D[Int]
+d.output
index 2862a61..d4f7b94 100644 (file)
@@ -33,3 +33,13 @@ class G
        fun md: Array[Fail] do return 0
 end
 
+var e = new E
+e.output
+var f = new F
+f.output
+var g = new G
+g.a.output
+g.ma(1)
+g.mb(1)
+g.mc(1).output
+g.md(1).output
index 8620254..4d9517f 100644 (file)
@@ -15,3 +15,4 @@
 # limitations under the License.
 
 var i = new Canard
+i.output
diff --git a/tests/sav/file_server_on_port_80.res b/tests/sav/file_server_on_port_80.res
deleted file mode 100644 (file)
index 1d69303..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-Mandatory option -u, --usergroup not found.
-Usage: file_server [Options]
-  -u, --usergroup   Drop privileges to user:group or simply user
-  --help, -h        Print this message
similarity index 98%
rename from tests/sav/neo_doxygen_dump_args4.res
rename to tests/sav/fixme/neo_doxygen_dump_args4.res
index a732359..66e13f0 100644 (file)
@@ -1457,7 +1457,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 ----
 =to=Entity#0:
 =labels=Array(4):
@@ -1481,7 +1481,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 ----
 =to=Entity#0:
 =labels=Array(4):
@@ -1722,7 +1722,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 
 
 Edge
similarity index 98%
rename from tests/sav/neo_doxygen_dump_args5.res
rename to tests/sav/fixme/neo_doxygen_dump_args5.res
index 5e63fb7..33a8156 100644 (file)
@@ -1457,7 +1457,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 ----
 =to=Entity#0:
 =labels=Array(4):
@@ -1481,7 +1481,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 ----
 =to=Entity#0:
 =labels=Array(4):
@@ -1722,7 +1722,7 @@ Edge
 8:MPropDef
 13:MAttributeDef
 =properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
 
 
 Edge
diff --git a/tests/sav/simple_file_server.res b/tests/sav/simple_file_server.res
new file mode 100644 (file)
index 0000000..12d09fe
--- /dev/null
@@ -0,0 +1 @@
+libevent warning: Opening localhost:80 failed
index aaadfa3..7da4254 100644 (file)
Binary files a/tests/sav/test_json_static.res and b/tests/sav/test_json_static.res differ
diff --git a/tests/sav/test_json_unicode.res b/tests/sav/test_json_unicode.res
new file mode 100644 (file)
index 0000000..c0f61cc
--- /dev/null
@@ -0,0 +1,5 @@
+{
+       "beer": "test",
+       "name": "Gaëa"
+}
+
diff --git a/tests/test_json_unicode.nit b/tests/test_json_unicode.nit
new file mode 100644 (file)
index 0000000..8800f3e
--- /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 json
+
+var str = """{
+       "beer": "test",
+       "name": "Gaëa"
+}"""
+
+var json = str.parse_json
+
+assert json != null
+
+print json.to_pretty_json