Merge: Faster buffers
authorJean Privat <jean@pryen.org>
Tue, 10 Mar 2015 13:51:03 +0000 (20:51 +0700)
committerJean Privat <jean@pryen.org>
Tue, 10 Mar 2015 13:51:03 +0000 (20:51 +0700)
Make flatbuffers a little faster on substrings

for nitc/nitc/nitc
before: 0m7.168s
after: 0m7.068s (-1.4%)

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

24 files changed:
lib/poset.nit
lib/standard/collection/abstract_collection.nit
lib/standard/collection/array.nit
lib/standard/collection/hash_collection.nit
lib/standard/file.nit
lib/standard/kernel.nit
lib/standard/stream.nit
lib/standard/string.nit
misc/README.md
misc/vim/plugin/nit.vim
src/astvalidation.nit
src/compiler/abstract_compiler.nit
src/doc/vim_autocomplete.nit
src/frontend/serialization_phase.nit
src/nitserial.nit
src/parser/parser_nodes.nit
src/phase.nit
src/semantize/auto_super_init.nit
tests/base_init_raf2.nit [new file with mode: 0644]
tests/sav/base_init_raf2.res [new file with mode: 0644]
tests/sav/nitserial_args1.res
tests/sav/nituml_args3.res
tests/sav/nituml_args4.res
tests/sav/test_new_native_alt1.res

index 93afcf1..3288e5c 100644 (file)
@@ -149,20 +149,37 @@ class POSet[E]
                # Update the transitive reduction
                if te.tos.has(f) then return # Skip the reduction if there is a loop
 
-               for x in te.dfroms.to_a do
+               # Remove transitive edges.
+               # Because the sets of direct is iterated, the list of edges to remove
+               # is stored and is applied after the iteration.
+               # The usual case is that no direct edges need to be removed,
+               # so start with a `null` list of edges.
+               var to_remove: nullable Array[E] = null
+               for x in te.dfroms do
                        var xe = self.elements[x]
                        if xe.tos.has(f) then
-                               te.dfroms.remove(x)
+                               if to_remove == null then to_remove = new Array[E]
+                               to_remove.add x
                                xe.dtos.remove(t)
                        end
                end
-               for x in fe.dtos.to_a do
+               if to_remove != null then
+                       for x in to_remove do te.dfroms.remove(x)
+                       to_remove.clear
+               end
+
+               for x in fe.dtos do
                        var xe = self.elements[x]
                        if xe.froms.has(t) then
                                xe.dfroms.remove(f)
-                               fe.dtos.remove(x)
+                               if to_remove == null then to_remove = new Array[E]
+                               to_remove.add x
                        end
                end
+               if to_remove != null then
+                       for x in to_remove do fe.dtos.remove(x)
+               end
+
                fe.dtos.add t
                te.dfroms.add f
        end
index 20d1584..d38e001 100644 (file)
@@ -416,7 +416,14 @@ interface MapRead[K, V]
                return default
        end
 
-       # Alias for `keys.has`
+       # Is there an item associated with `key`?
+       #
+       #     var x = new HashMap[String, Int]
+       #     x["four"] = 4
+       #     assert x.has_key("four") == true
+       #     assert x.has_key("five") == false
+       #
+       # By default it is a synonymous to `keys.has` but could be redefined with a direct implementation.
        fun has_key(key: K): Bool do return self.keys.has(key)
 
        # Get a new iterator on the map.
@@ -987,6 +994,8 @@ interface CoupleMap[K, V]
                        return c.second
                end
        end
+
+       redef fun has_key(key) do return couple_at(key) != null
 end
 
 # Iterator on CoupleMap
index 1fb8581..233621e 100644 (file)
@@ -130,7 +130,20 @@ abstract class AbstractArrayRead[E]
                end
        end
 
-       redef fun iterator: ArrayIterator[E] do return new ArrayIterator[E](self)
+       redef fun iterator: ArrayIterator[E] do
+               var res = _free_iterator
+               if res == null then return new ArrayIterator[E](self)
+               res._index = 0
+               _free_iterator = null
+               return res
+       end
+
+       # An old iterator, free to reuse.
+       # Once an iterator is `finish`, it become reusable.
+       # Since some arrays are iterated a lot, this avoid most of the
+       # continuous allocation/garbage-collection of the needed iterators.
+       private var free_iterator: nullable ArrayIterator[E] = null
+
        redef fun reverse_iterator do return new ArrayReverseIterator[E](self)
 end
 
@@ -477,6 +490,8 @@ private class ArrayIterator[E]
        redef var index = 0
 
        var array: AbstractArrayRead[E]
+
+       redef fun finish do _array._free_iterator = self
 end
 
 private class ArrayReverseIterator[E]
@@ -618,8 +633,8 @@ class ArrayMap[K, E]
                end
        end
 
-       redef var keys: RemovableCollection[K] = new ArrayMapKeys[K, E](self)
-       redef var values: RemovableCollection[E] = new ArrayMapValues[K, E](self)
+       redef var keys: RemovableCollection[K] = new ArrayMapKeys[K, E](self) is lazy
+       redef var values: RemovableCollection[E] = new ArrayMapValues[K, E](self) is lazy
 
        # O(1)
        redef fun length do return _items.length
index b62d876..e5ff507 100644 (file)
@@ -272,8 +272,9 @@ class HashMap[K, V]
                enlarge(0)
        end
 
-       redef var keys: RemovableCollection[K] = new HashMapKeys[K, V](self)
-       redef var values: RemovableCollection[V] = new HashMapValues[K, V](self)
+       redef var keys: RemovableCollection[K] = new HashMapKeys[K, V](self) is lazy
+       redef var values: RemovableCollection[V] = new HashMapValues[K, V](self) is lazy
+       redef fun has_key(k) do return node_at(k) != null
 end
 
 # View of the keys of a HashMap
index 2009a50..4d77955 100644 (file)
@@ -254,6 +254,7 @@ class Stdout
                _file = new NativeFile.native_stdout
                path = "/dev/stdout"
                _is_writable = true
+               set_buffering_mode(256, sys.buffer_mode_line)
        end
 end
 
@@ -998,18 +999,14 @@ end
 
 redef class Sys
 
-       init do
-               if stdout isa FileStream then stdout.as(FileStream).set_buffering_mode(256, buffer_mode_line)
-       end
-
        # Standard input
-       var stdin: PollableReader = new Stdin is protected writable
+       var stdin: PollableReader = new Stdin is protected writable, lazy
 
        # Standard output
-       var stdout: Writer = new Stdout is protected writable
+       var stdout: Writer = new Stdout is protected writable, lazy
 
        # Standard output for errors
-       var stderr: Writer = new Stderr is protected writable
+       var stderr: Writer = new Stderr is protected writable, lazy
 
        # Enumeration for buffer mode full (flushes when buffer is full)
        fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0"
index 21b0fc1..01e72e3 100644 (file)
@@ -718,6 +718,22 @@ universal Char
        do
                return is_lower or is_upper
        end
+
+       # Is self a whitespace character?
+       #
+       # These correspond to the "Other" and "Separator" groups of the Unicode.
+       #
+       # In the ASCII encoding, this is those <= to space (0x20) plus delete (0x7F).
+       #
+       #     assert 'A'.is_whitespace  == false
+       #     assert ','.is_whitespace  == false
+       #     assert ' '.is_whitespace  == true
+       #     assert '\t'.is_whitespace == true
+       fun is_whitespace: Bool
+       do
+               var i = ascii
+               return i <= 0x20 or i == 0x7F
+       end
 end
 
 # Pointer classes are used to manipulate extern C structures.
index 6079d95..7df1bd1 100644 (file)
@@ -220,6 +220,66 @@ abstract class Reader
        # Is there something to read.
        # This function returns 'false' if there is something to read.
        fun eof: Bool is abstract
+
+       # Read the next sequence of non whitespace characters.
+       #
+       # Leading whitespace characters are skipped.
+       # The first whitespace character that follows the result is consumed.
+       #
+       # An empty string is returned if the end of the file or an error is encounter.
+       #
+       # ~~~
+       # var w = new StringReader(" Hello, \n\t World!")
+       # assert w.read_word == "Hello,"
+       # assert w.read_char == '\n'.ascii
+       # assert w.read_word == "World!"
+       # assert w.read_word == ""
+       # ~~~
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
+       fun read_word: String
+       do
+               var buf = new FlatBuffer
+               var c = read_nonwhitespace
+               if c > 0 then
+                       buf.add(c.ascii)
+                       while not eof do
+                               c = read_char
+                               if c < 0 then break
+                               var a = c.ascii
+                               if a.is_whitespace then break
+                               buf.add(a)
+                       end
+               end
+               var res = buf.to_s
+               return res
+       end
+
+       # Skip whitespace characters (if any) then return the following non-whitespace character.
+       #
+       # Returns the code point of the character.
+       # Return -1 on end of file or error.
+       #
+       # In fact, this method works like `read_char` except it skips whitespace.
+       #
+       # ~~~
+       # var w = new StringReader(" \nab\tc")
+       # assert w.read_nonwhitespace == 'a'.ascii
+       # assert w.read_nonwhitespace == 'b'.ascii
+       # assert w.read_nonwhitespace == 'c'.ascii
+       # assert w.read_nonwhitespace == -1
+       # ~~~
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
+       fun read_nonwhitespace: Int
+       do
+               var c = -1
+               while not eof do
+                       c = read_char
+                       if c < 0 or not c.ascii.is_whitespace then break
+               end
+               return c
+       end
 end
 
 # Iterator returned by `Reader::each_line`.
index d3aa602..e78f655 100644 (file)
@@ -350,12 +350,12 @@ abstract class Text
        #
        #     assert " \n\thello \n\t".l_trim == "hello \n\t"
        #
-       # A whitespace is defined as any character which ascii value is less than or equal to 32
+       # `Char::is_whitespace` determines what is a whitespace.
        fun l_trim: SELFTYPE
        do
                var iter = self.chars.iterator
                while iter.is_ok do
-                       if iter.item.ascii > 32 then break
+                       if not iter.item.is_whitespace then break
                        iter.next
                end
                if iter.index == length then return self.empty
@@ -366,12 +366,12 @@ abstract class Text
        #
        #     assert " \n\thello \n\t".r_trim == " \n\thello"
        #
-       # A whitespace is defined as any character which ascii value is less than or equal to 32
+       # `Char::is_whitespace` determines what is a whitespace.
        fun r_trim: SELFTYPE
        do
                var iter = self.chars.reverse_iterator
                while iter.is_ok do
-                       if iter.item.ascii > 32 then break
+                       if not iter.item.is_whitespace then break
                        iter.next
                end
                if iter.index < 0 then return self.empty
@@ -379,12 +379,29 @@ abstract class Text
        end
 
        # Trims trailing and preceding white spaces
-       # A whitespace is defined as any character which ascii value is less than or equal to 32
        #
        #     assert "  Hello  World !  ".trim   == "Hello  World !"
        #     assert "\na\nb\tc\t".trim          == "a\nb\tc"
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
        fun trim: SELFTYPE do return (self.l_trim).r_trim
 
+       # Is the string non-empty but only made of whitespaces?
+       #
+       #    assert " \n\t ".is_whitespace    == true
+       #    assert "  hello  ".is_whitespace == false
+       #    assert "".is_whitespace          == false
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
+       fun is_whitespace: Bool
+       do
+               if is_empty then return false
+               for c in self.chars do
+                       if not c.is_whitespace then return false
+               end
+               return true
+       end
+
        # Returns `self` removed from its last line terminator (if any).
        #
        #    assert "Hello\n".chomp == "Hello"
@@ -1052,7 +1069,7 @@ class FlatString
        # Indes in _items of the last item of the string
        private var index_to: Int is noinit
 
-       redef var chars: SequenceRead[Char] = new FlatStringCharView(self)
+       redef var chars: SequenceRead[Char] = new FlatStringCharView(self) is lazy
 
        redef fun [](index)
        do
@@ -1526,7 +1543,7 @@ class FlatBuffer
        super FlatText
        super Buffer
 
-       redef var chars: Sequence[Char] = new FlatBufferCharView(self)
+       redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
 
        private var capacity: Int = 0
 
index 181c047..1661f43 100644 (file)
@@ -37,6 +37,8 @@ Ensure that `~/.vimrc` contains
  * Automatic indentation
  * Syntax checker (require [Syntastic][2]).
  * Autocomplete for whole projects using module importations
+ * Show documentation in preview window
+ * Search declarations and usages of the word under the cursor
 
   [2]: https://github.com/scrooloose/syntastic
 
@@ -93,3 +95,25 @@ will use general metadata in the plugin directory.
 
 The metadata files from nitpick are stored in `~/.vim/nit/`. This location can be customized with
 the environment variable `NIT_VIM_DIR`.
+
+## Documentation in preview window
+
+You can display the documentation for the entity under the cursor with `:call Nitdoc()`.
+It will use the same metadata files as the omnifunc and the preview window.
+You may want to map the function to a shortcut by adding the following code to `~/.vimrc`.
+
+~~~
+" Map displaying Nitdoc to Ctrl-D
+map <C-d> :call Nitdoc()<enter>
+~~~
+
+## Search declarations and usages of the word under the cursor
+
+The function `NitGitGrep` calls `git grep` to find declarations and usages of the word under the cursor.
+It displays the results in the preview window.
+You may want to map the function to a shortcut by adding the following code to `~/.vimrc`.
+
+~~~
+" Map the NitGitGrep function to Ctrl-G
+map <C-g> :call NitGitGrep()<enter>
+~~~
index 23e557a..046ab9f 100644 (file)
@@ -81,17 +81,10 @@ function ForceNitComplete()
        call NitComplete()
 endfunction
 
-" Internal function to search for lines in `path` corresponding to the partial
-" word `base`. Adds found and formated match to `matches`.
+" Get path to the best metadata file named `name`
 "
-" Will order the results in 3 levels:
-" 1. Exact matches
-" 2. Common prefix matches
-" 3. Substring matches
-fun NitOmnifuncAddFromFile(base, matches, path)
-       let prefix_matches = []
-       let substring_matches = []
-
+" Returns an empty string if not found.
+fun NitMetadataFile(name)
        " Where are the generated metadata files?
        if empty($NIT_VIM_DIR)
                let metadata_dir = $HOME . '/.vim/nit'
@@ -99,17 +92,41 @@ fun NitOmnifuncAddFromFile(base, matches, path)
                let metadata_dir = $NIT_VIM_DIR
        end
 
-       let path = metadata_dir . '/' . a:path
+       let path = metadata_dir . '/' . a:name
+
        " Is there generated custom metadata files?
        if ! filereadable(path)
-               let path = s:script_dir . '/' . a:path
+               let path = s:script_dir . '/' . a:name
 
                " Is there standard metadata files?
                if ! filereadable(path)
-                       return
+                       return ''
                endif
        endif
 
+       return path
+endfun
+
+" Internal function to search for lines in `path` corresponding to the partial
+" word `base`. Adds found and formated match to `matches`.
+"
+" Will order the results in 5 levels:
+" 1. Exact matches
+" 2. Common prefix matches
+" 3. Substring matches
+" 4. Synopsis matches
+" 5. Doc matches
+fun NitOmnifuncAddFromFile(base, matches, path)
+       let prefix_matches = []
+       let substring_matches = []
+       let synopsis_matches = []
+       let doc_matches = []
+
+       let path = NitMetadataFile(a:path)
+       if empty(path)
+               return
+       endif
+
        for line in readfile(path)
                let words = split(line, '#====#', 1)
                let name = get(words, 0, '')
@@ -118,18 +135,26 @@ fun NitOmnifuncAddFromFile(base, matches, path)
                if name == a:base
                        " Exact match
                        call NitOmnifuncAddAMatch(a:matches, words, name)
-               elseif name =~ '^'.a:base
+               elseif name =~? '^'.a:base
                        " Common-prefix match
                        call NitOmnifuncAddAMatch(prefix_matches, words, name)
-               elseif name =~ a:base
+               elseif name =~? a:base
                        " Substring match
                        call NitOmnifuncAddAMatch(substring_matches, words, name)
+               elseif get(words, 2, '') =~? a:base
+                       " Match in the synopsis
+                       call NitOmnifuncAddAMatch(synopsis_matches, words, name)
+               elseif get(words, 3, '') =~? a:base
+                       " Match in the longer doc
+                       call NitOmnifuncAddAMatch(synopsis_matches, words, name)
                endif
        endfor
 
        " Assemble the final match list
        call extend(a:matches, sort(prefix_matches))
        call extend(a:matches, sort(substring_matches))
+       call extend(a:matches, sort(synopsis_matches))
+       call extend(a:matches, sort(doc_matches))
 endfun
 
 " Internal function to search parse the information from a metadata line
@@ -159,10 +184,10 @@ fun NitOmnifunc(findstart, base)
                " find keyword matching with "a:base"
                let matches = []
 
-               " Advanced suggestions
+               " advanced suggestions
                let cursor_line = getline('.')
 
-               " Content of the line before the partial word
+               " content of the line before the partial word
                let line_prev_cursor = cursor_line[:col('.')-1]
 
                let prev_char_at = strlen(line_prev_cursor) - 1
@@ -240,6 +265,89 @@ fun NitOmnifunc(findstart, base)
        endif
 endfun
 
+" Show doc for the entity under the cursor in the preview window
+fun Nitdoc()
+       " Word under cursor
+       let word = expand("<cword>")
+
+       " All possible docs (there may be more than one entity with the same name)
+       let docs = []
+
+       " Search in all metadata files
+       for file in ['modules', 'classes', 'properties']
+               let path = NitMetadataFile(file.'.txt')
+               if empty(path)
+                       continue
+               endif
+
+               for line in readfile(path)
+                       let words = split(line, '#====#', 1)
+                       let name = get(words, 0, '')
+                       if name =~ '^' . word
+                               " It fits our word, get long doc
+                               let desc = get(words,3,'')
+                               let desc = join(split(desc, '#nnnn#', 1), "\n")
+                               call add(docs, desc)
+                       endif
+               endfor
+       endfor
+
+       " Found no doc, give up
+       if empty(docs) || !(join(docs, '') =~ '\w')
+               return
+       endif
+
+       " Open the preview window on a temp file
+       execute "silent pedit " . tempname()
+
+       " Change to preview window
+       wincmd P
+
+       " Show all found doc one after another
+       for doc in docs
+               if doc =~ '\w'
+                       silent put = doc
+                       silent put = ''
+               endif
+       endfor
+
+       " Set options
+       setlocal buftype=nofile
+       setlocal noswapfile
+       setlocal syntax=none
+       setlocal bufhidden=delete
+
+       " Change back to the source buffer
+       wincmd p
+       redraw!
+endfun
+
+" Call `git grep` on the word under the cursor
+"
+" Shows declarations first, then all matches, in the preview window.
+fun NitGitGrep()
+       let word = expand("<cword>")
+       let out = tempname()
+       execute 'silent !(git grep "\\(module\\|class\\|universal\\|interface\\|var\\|fun\\) '.word.'";'.
+               \'echo; git grep '.word.') > '.out
+
+       " Open the preview window on a temp file
+       execute "silent pedit " . out
+
+       " Change to preview window
+       wincmd P
+
+       " Set options
+       setlocal buftype=nofile
+       setlocal noswapfile
+       setlocal syntax=none
+       setlocal bufhidden=delete
+
+       " Change back to the source buffer
+       wincmd p
+       redraw!
+endfun
+
 " Activate the omnifunc on Nit files
 autocmd FileType nit set omnifunc=NitOmnifunc
 
index b568366..b34d42f 100644 (file)
@@ -22,9 +22,7 @@ class ASTValidationVisitor
        super Visitor
        redef fun visit(node)
        do
-               path.unshift(node)
                node.accept_ast_validation(self)
-               path.shift
        end
        private var path = new List[ANode]
        private var seen = new HashSet[ANode]
@@ -34,29 +32,33 @@ redef class ANode
        private fun accept_ast_validation(v: ASTValidationVisitor)
        do
                var parent = self.parent
+               var path = v.path
 
-               if v.path.length > 1 then
-                       var path_parent = v.path[1]
+               if path.length > 0 then
+                       var path_parent = v.path.first
                        if parent == null then
                                self.parent = path_parent
                                #debug "PARENT: expected parent: {path_parent}"
+                               v.seen.add(self)
                        else if parent != path_parent then
                                self.parent = path_parent
-                               debug "PARENT: expected parent: {path_parent}, got {parent}"
+                               if v.seen.has(self) then
+                                       debug "DUPLICATE (NOTATREE): already seen node with parent {parent} now with {path_parent}."
+                               else
+                                       v.seen.add(self)
+                                       debug "PARENT: expected parent: {path_parent}, got {parent}"
+                               end
                        end
                end
 
-               if v.seen.has(self) then
-                       debug "DUPLICATE: already seen node. NOTATREE"
-               end
-               v.seen.add(self)
-
                if not isset _location then
                        #debug "LOCATION: unlocated node {v.path.join(", ")}"
                        _location = self.parent.location
                end
 
+               path.unshift(self)
                visit_all(v)
+               path.shift
        end
 end
 
index b53a7a2..23927b6 100644 (file)
@@ -2304,7 +2304,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
index 10c6bc0..b7edca0 100644 (file)
@@ -71,10 +71,10 @@ redef class MEntity
 
                # 4. Full doc with extra
                stream.write field_separator
+               stream.write "# "
+               stream.write full_name
+               write_signature_to_stream(stream)
                if mdoc != null then
-                       stream.write "# "
-                       stream.write full_name
-                       write_signature_to_stream(stream)
                        for i in 2.times do stream.write line_separator
                        stream.write mdoc.content.join(line_separator)
                end
index 75863d9..2b68eea 100644 (file)
@@ -97,8 +97,11 @@ private class SerializationPhasePreModel
        end
 
        # Add a constructor to the automated nclassdef
-       fun generate_deserialization_init(nclassdef: AClassdef)
+       fun generate_deserialization_init(nclassdef: AStdClassdef)
        do
+               # Do not generate constructors for abstract classes
+               if nclassdef.n_classkind isa AAbstractClasskind then return
+
                var npropdefs = nclassdef.n_propdefs
 
                var code = new Array[String]
@@ -156,7 +159,9 @@ private class SerializationPhasePreModel
 
                for nclassdef in nclassdefs do
                        var name = nclassdef.n_id.text
-                       if nclassdef.n_formaldefs.is_empty then
+                       if nclassdef.n_formaldefs.is_empty and
+                               not nclassdef.n_classkind isa AAbstractClasskind then
+
                                code.add "              if name == \"{name}\" then return new {name}.from_deserializer(self)"
                        end
                end
index ea2e4b4..db70d4c 100644 (file)
@@ -69,9 +69,13 @@ redef class ToolContext
        # Where do we put the result?
        var opt_dir: OptionString = new OptionString("Output directory", "--dir")
 
+       # Depth of the visit and generation
+       var opt_depth = new OptionEnum(["module", "group", "project"],
+               "Depth of the visit and generation", 0, "-d", "--depth")
+
        redef init
        do
-               option_context.add_option(opt_output, opt_dir)
+               option_context.add_option(opt_output, opt_dir, opt_depth)
                super
        end
 end
@@ -130,10 +134,8 @@ var modelbuilder = new ModelBuilder(model, toolcontext)
 var mmodules = modelbuilder.parse_full(arguments)
 modelbuilder.run_phases
 
-# Create a distinct support module per targetted modules
+# Create a distinct support module per target modules
 for mmodule in mmodules do
-       var rta = modelbuilder.do_rapid_type_analysis(mmodule)
-
        # Name of the support module
        var module_name
 
@@ -155,13 +157,47 @@ for mmodule in mmodules do
                module_path += ".nit"
        end
 
+       var target_modules = null
+       var importations = null
+       var mgroup = mmodule.mgroup
+       if toolcontext.opt_depth.value == 1 and mgroup != null then
+               modelbuilder.visit_group mgroup
+               target_modules = mgroup.mmodules
+       else if toolcontext.opt_depth.value == 2 then
+               # project
+               target_modules = new Array[MModule]
+               importations = new Array[MModule]
+               if mgroup != null then
+                       for g in mgroup.mproject.mgroups do
+                               target_modules.add_all g.mmodules
+                       end
+
+                       for g in mgroup.in_nesting.direct_smallers do
+                               var dm = g.default_mmodule
+                               if dm != null then
+                                       importations.add dm
+                               end
+                       end
+
+                       for m in mgroup.mmodules do
+                               importations.add m
+                       end
+               end
+       end
+
+       if target_modules == null then target_modules = [mmodule]
+       if importations == null then importations = target_modules
+
        var nit_module = new NitModule(module_name)
        nit_module.header = """
 # This file is generated by nitserial
 # Do not modify, but you can redef
 """
 
-       nit_module.imports.add mmodule.name
+       for importation in importations do
+               nit_module.imports.add importation.name
+       end
+
        nit_module.imports.add "serialization"
 
        nit_module.content.add """
@@ -170,15 +206,25 @@ redef class Deserializer
        do"""
 
        var serializable_type = mmodule.serializable_type
-       for mtype in rta.live_types do
-               # We are only interested in instanciated generics, subtypes of Serializable
-               # and which are visibles.
-               if mtype isa MGenericType and
-                  mtype.is_subtype(mmodule, null, serializable_type) and
-                  mtype.is_visible_from(mmodule) then
-
-                       nit_module.content.add """
+       var compiled_types = new Array[MType]
+       for m in target_modules do
+               nit_module.content.add """
+               # Module: {{{m.to_s}}}"""
+
+               var rta = modelbuilder.do_rapid_type_analysis(m)
+
+               for mtype in rta.live_types do
+                       # We are only interested in instanciated generics, subtypes of Serializable
+                       # and which are visibles.
+                       if mtype isa MGenericType and
+                          mtype.is_subtype(m, null, serializable_type) and
+                          mtype.is_visible_from(mmodule) and
+                          not compiled_types.has(mtype) then
+
+                               compiled_types.add mtype
+                               nit_module.content.add """
                if name == \"{{{mtype}}}\" then return new {{{mtype}}}.from_deserializer(self)"""
+                       end
                end
        end
 
index 270c185..1cadb05 100644 (file)
@@ -1598,7 +1598,7 @@ class ALabel
        var n_kwlabel: TKwlabel is writable, noinit
 
        # The name of the label, if any
-       var n_id: nullable TId is writable
+       var n_id: nullable TId is writable, noinit
 end
 
 # Expression and statements
@@ -2222,7 +2222,7 @@ class ASelfExpr
        super AExpr
 
        # The `self` keyword
-       var n_kwself: nullable TKwself is writable
+       var n_kwself: nullable TKwself = null is writable
 end
 
 # When there is no explicit receiver, `self` is implicit
index 0a32484..bdec532 100644 (file)
@@ -110,7 +110,6 @@ redef class ToolContext
 
                        for phase in phases do
                                if phase.disabled then continue
-                               self.info(" phase: {phase}", 3)
                                assert phase.toolcontext == self
                                var errcount = self.error_count
                                phase.process_nmodule(nmodule)
index b16eaf5..d60e2a3 100644 (file)
@@ -71,9 +71,6 @@ redef class AMethPropdef
                        return
                end
 
-               # FIXME: THIS IS STUPID (be here to keep the old code working)
-               if not mpropdef.mclassdef.is_intro then return
-
                # Do we inherit for a constructor?
                var skip = true
                for cd in mclassdef.in_hierarchy.direct_greaters do
@@ -102,6 +99,7 @@ redef class AMethPropdef
                if not mpropdef.is_intro then
                        auto_super_call = true
                        mpropdef.has_supercall = true
+                       modelbuilder.toolcontext.info("Auto-super call for {mpropdef}", 4)
                        return
                end
 
@@ -136,6 +134,7 @@ redef class AMethPropdef
 
                        var callsite = new CallSite(self, recvtype, mmodule, anchor, true, candidate, candidatedef, msignature, false)
                        auto_super_inits.add(callsite)
+                       modelbuilder.toolcontext.info("Old-style auto-super init for {mpropdef} to {candidate.full_name}", 4)
                end
 
                # No old style? The look for new-style super constructors (called from a old style constructor)
@@ -170,6 +169,7 @@ redef class AMethPropdef
 
                        var callsite = new CallSite(self, recvtype, mmodule, anchor, true, the_root_init_mmethod, candidatedef, msignature, false)
                        auto_super_inits.add(callsite)
+                       modelbuilder.toolcontext.info("Auto-super init for {mpropdef} to {the_root_init_mmethod.full_name}", 4)
                end
                if auto_super_inits.is_empty then
                        modelbuilder.error(self, "Error: No constructors to call implicitely in {mpropdef}. Call one explicitely.")
diff --git a/tests/base_init_raf2.nit b/tests/base_init_raf2.nit
new file mode 100644 (file)
index 0000000..fd4ee5b
--- /dev/null
@@ -0,0 +1,36 @@
+# 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 base_init
+
+redef class A
+       init
+       do
+               'a'.output
+       end
+end
+
+redef class B
+       init
+       do
+               'b'.output
+       end
+end
+
+redef class C
+       init
+       do
+               'c'.output
+       end
+end
diff --git a/tests/sav/base_init_raf2.res b/tests/sav/base_init_raf2.res
new file mode 100644 (file)
index 0000000..b64a053
--- /dev/null
@@ -0,0 +1,3 @@
+Aa
+AaBb
+Aac
index 62be912..b69dc33 100644 (file)
@@ -8,6 +8,7 @@ import serialization
 redef class Deserializer
        redef fun deserialize_class(name)
        do
+               # Module: test_serialization
                if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
                if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
                if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
index 24adbed..94b3764 100644 (file)
@@ -57,7 +57,7 @@ Discrete -> Int [dir=back arrowtail=open style=dashed];
 Numeric -> Int [dir=back arrowtail=open style=dashed];
 
 Char [
- label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l}"
+ label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l+ is_whitespace(): Bool\l}"
 ]
 Discrete -> Char [dir=back arrowtail=open style=dashed];
 
index 81077d2..1664fce 100644 (file)
@@ -57,7 +57,7 @@ Discrete -> Int [dir=back arrowtail=open style=dashed];
 Numeric -> Int [dir=back arrowtail=open style=dashed];
 
 Char [
- label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l}"
+ label = "{Char||+ to_i(): Int\l+ ascii(): Int\l+ to_lower(): Char\l+ to_upper(): Char\l+ is_digit(): Bool\l+ is_lower(): Bool\l+ is_upper(): Bool\l+ is_letter(): Bool\l+ is_whitespace(): Bool\l}"
 ]
 Discrete -> Char [dir=back arrowtail=open style=dashed];
 
index cf299e7..1fb7aeb 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:894)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:909)
 NativeString
 N
 Nit