Merge: metrics: `--nullables` distinguishes safe and unsafe calls on `null`
authorJean Privat <jean@pryen.org>
Sat, 23 May 2015 01:01:37 +0000 (21:01 -0400)
committerJean Privat <jean@pryen.org>
Sat, 23 May 2015 01:01:37 +0000 (21:01 -0400)
In reaction of recent updates on #394 I wanted to have some up-to-date numbers.

`nitmetrics --nullable` can count the sends on nullable receivers but safe and unsafe calls where merged. Note: safe calls on nullable are things like `x == null`, that is safe even if `x` is a nullable thing.
So this PR distinguishes safe and unsafe calls.

Some numbers:

~~~
nitmetrics --nullables ../lib ../src/nit*.nit
Total number of sends: 60262
Number of sends on a unsafe nullable receiver: 1750 (2.90%)
Number of sends on a safe nullable receiver: 2639 (4.37%)
~~~

Pull-Request: #1375
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

32 files changed:
contrib/benitlux/src/benitlux_controller.nit
contrib/nitiwiki/src/wiki_links.nit
lib/android/bundle/bundle.nit
lib/android/intent/intent_api10.nit
lib/android/shared_preferences/shared_preferences_api10.nit
lib/json/serialization.nit [moved from lib/json_serialization.nit with 53% similarity]
lib/linux/data_store.nit
lib/markdown/decorators.nit [new file with mode: 0644]
lib/markdown/markdown.nit
lib/markdown/wikilinks.nit [new file with mode: 0644]
lib/mpi.nit
lib/serialization/engine_tools.nit [new file with mode: 0644]
lib/standard/file.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/model/model.nit
tests/sav/nitg-e/test_deserialization_serial.res [deleted file]
tests/sav/nitg-e/test_json_deserialization_alt1.res [moved from tests/sav/nitg-e/test_deserialization.res with 100% similarity]
tests/sav/nitg-e/test_new_native.res
tests/sav/nitg-e/test_new_native_alt1.res
tests/sav/nitserial_args1.res
tests/sav/test_deserialization.res [deleted file]
tests/sav/test_json_deserialization.res [moved from tests/sav/test_deserialization_alt1.res with 100% similarity]
tests/sav/test_json_deserialization_alt1.res [moved from tests/sav/test_deserialization_serial.res with 100% similarity]
tests/sav/test_json_deserialization_alt2.res [new file with mode: 0644]
tests/sav/test_new_native.res
tests/sav/test_serialization_alt1.res [new file with mode: 0644]
tests/test_deserialization.nit
tests/test_deserialization_serial.nit
tests/test_json_deserialization.nit [new file with mode: 0644]
tests/test_new_native.nit
tests/test_serialization.nit

index 2297bbd..f124315 100644 (file)
@@ -18,7 +18,7 @@
 module benitlux_controller
 
 import nitcorn
-import json_serialization
+private import json::serialization
 
 import benitlux_model
 import benitlux_db
index bbbfea0..bfdeb86 100644 (file)
@@ -16,7 +16,7 @@
 module wiki_links
 
 import wiki_base
-intrude import markdown
+import markdown::wikilinks
 
 redef class Nitiwiki
        # Looks up a WikiEntry by its `name`.
@@ -189,16 +189,6 @@ class NitiwikiMdProcessor
                emitter = new MarkdownEmitter(self)
                emitter.decorator = new NitiwikiDecorator(wiki, context)
        end
-
-       redef fun token_at(text, pos) do
-               var token = super
-               if not token isa TokenLink then return token
-               if pos + 1 < text.length then
-                       var c = text[pos + 1]
-                       if c == '[' then return new TokenWikiLink(pos, c)
-               end
-               return token
-       end
 end
 
 private class NitiwikiDecorator
@@ -210,7 +200,7 @@ private class NitiwikiDecorator
        # Article used to contextualize links.
        var context: WikiArticle
 
-       fun add_wikilink(v: MarkdownEmitter, link: Text, name, comment: nullable Text) do
+       redef fun add_wikilink(v, link, name, comment) do
                var wiki = v.processor.as(NitiwikiMdProcessor).wiki
                var target: nullable WikiEntry = null
                var anchor: nullable String = null
@@ -250,46 +240,3 @@ private class NitiwikiDecorator
                v.add "</a>"
        end
 end
-
-# A NitiWiki link token.
-#
-# Something of the form `[[foo]]`.
-#
-# Allowed formats:
-#
-# * `[[Wikilink]]`
-# * `[[Wikilink/Bar]]`
-# * `[[Wikilink#foo]]`
-# * `[[Wikilink/Bar#foo]]`
-# * `[[title|Wikilink]]`
-# * `[[title|Wikilink/Bar]]`
-# * `[[title|Wikilink/Bar#foo]]`
-class TokenWikiLink
-       super TokenLink
-
-       redef fun emit_hyper(v) do
-               v.decorator.as(NitiwikiDecorator).add_wikilink(v, link.as(not null), name, comment)
-       end
-
-       redef fun check_link(v, out, start, token) do
-               var md = v.current_text
-               var pos = start + 2
-               var tmp = new FlatBuffer
-               pos = md.read_md_link_id(tmp, pos)
-               if pos < start then return -1
-               var name = tmp.write_to_string
-               if name.has("|") then
-                       var parts = name.split_once_on("|")
-                       self.name = parts.first
-                       self.link = parts[1]
-               else
-                       self.name = null
-                       self.link = name
-               end
-               pos += 1
-               pos = md.skip_spaces(pos)
-               if pos < start then return -1
-               pos += 1
-               return pos
-       end
-end
index 3678cbc..19b2f8e 100644 (file)
@@ -19,7 +19,7 @@
 module bundle
 
 import serialization
-import json_serialization
+import json::serialization
 
 import platform
 import activities
index 5f37179..5312c15 100644 (file)
@@ -21,7 +21,7 @@ module intent_api10
 import dalvik
 import android::bundle
 import serialization
-private import json_serialization
+private import json::serialization
 
 in "Java" `{
        import android.content.Intent;
index 72a634c..84d213d 100644 (file)
@@ -19,11 +19,11 @@ module shared_preferences_api10
 
 import dalvik
 import serialization
-private import json_serialization
+private import json::serialization
 
 in "Java" `{
        import android.content.SharedPreferences;
-       import android.content.Context; 
+       import android.content.Context;
        import android.app.Activity;
        import java.util.Map;
        import java.util.Iterator;
similarity index 53%
rename from lib/json_serialization.nit
rename to lib/json/serialization.nit
index b9667db..61c6f61 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Handles serialization and deserialization of objects to/from Json.
-module json_serialization
+# Handles serialization and deserialization of objects to/from JSON
+#
+# ## Nity JSON
+#
+# `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
+# and `JsonDeserializer` can read them. They both use meta-data added to the
+# generated JSON to recreate the Nit instances with the exact original type.
+#
+# For more information on Nit serialization, see: ../serialization/README.md
+#
+# ## Plain JSON
+#
+# The attribute `JsonSerializer::plain_json` triggers generating plain and
+# clean JSON. This format is easier to read for an human and a non-Nit program,
+# but it cannot be fully deserialized. It can still be read by services from
+# `json::static` and `json::dynamic`.
+#
+# A shortcut to this service is provided by `Serializable::to_plain_json`.
+#
+# ### Usage Example
+#
+# ~~~nitish
+# import json::serialization
+#
+# class Person
+#     auto_serializable
+#
+#     var name: String
+#     var year_of_birth: Int
+#     var next_of_kin: nullable Person
+# end
+#
+# var bob = new Person("Bob", 1986)
+# var alice = new Person("Alice", 1978, bob)
+#
+# assert bob.to_plain_json == """
+# {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
+#
+# assert alice.to_plain_json == """
+# {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
+# ~~~
+module serialization
 
-import serialization
-import json::static
+import ::serialization
+private import ::serialization::engine_tools
+private import static
 
 # Serializer of Nit objects to Json string.
 class JsonSerializer
@@ -27,25 +68,87 @@ class JsonSerializer
        # Target writing stream
        var stream: Writer
 
+       # Write plain JSON? easier to read but does not support Nit deserialization
+       #
+       # If `false`, the default, serialize to support deserialization:
+       #
+       # * Write meta-data, including the types of the serialized objects so they can
+       #   be deserialized to their original form using `JsonDeserializer`.
+       # * Use references when an object has already been serialized so to not duplicate it.
+       # * Support cycles in references.
+       # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
+       # * The generated JSON is standard and can be read by non-Nit programs.
+       #   However, some Nit types are not represented by the simplest possible JSON representation.
+       #   With the added meta-data, it can be complex to read.
+       #
+       # If `true`, serialize for other programs:
+       #
+       # * Nit objects are serialized to pure and standard JSON so they can
+       #   be easily read by non-Nit programs and humans.
+       # * Nit objects are serialized for every references, so they can be duplicated.
+       #   It is easier to read but it creates a larger output.
+       # * Does not support cycles, will replace the problematic references by `null`.
+       # * Does not serialize the meta-data needed to deserialize the objects
+       #   back to regular Nit objects.
+       # * Keys of Nit `HashMap` are converted to their string reprensentation using `to_s`.
+       var plain_json = false is writable
+
+       # List of the current open objects, the first is the main target of the serialization
+       #
+       # Used only when `plain_json == true` to detect cycles in serialization.
+       private var open_objects = new Array[Object]
+
+       # Has the first attribute of the current object already been serialized?
+       #
+       # Used only when `plain_json == true`.
+       private var first_attribute = false
+
        redef fun serialize(object)
        do
                if object == null then
                        stream.write "null"
-               else object.serialize_to_json(self)
+               else
+                       if plain_json then
+                               for o in open_objects do
+                                       if object.is_same_serialized(o) then
+                                               # Cycle detected
+                                               stream.write "null"
+                                               return
+                                       end
+                               end
+
+                               open_objects.add object
+                       end
+
+                       first_attribute = true
+                       object.serialize_to_json self
+                       first_attribute = false
+
+                       if plain_json then open_objects.pop
+               end
        end
 
        redef fun serialize_attribute(name, value)
        do
-               stream.write ", \"{name}\": "
+               if not plain_json or not first_attribute then
+                       stream.write ", "
+                       first_attribute = false
+               end
+
+               stream.write "\""
+               stream.write name
+               stream.write "\": "
                super
        end
 
        redef fun serialize_reference(object)
        do
-               if refs_map.has_key(object) then
+               if not plain_json and refs_map.has_key(object) then
                        # if already serialized, add local reference
                        var id = ref_id_for(object)
-                       stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}"
+                       stream.write "\{\"__kind\": \"ref\", \"__id\": "
+                       stream.write id.to_s
+                       stream.write "\}"
                else
                        # serialize here
                        serialize object
@@ -76,10 +179,10 @@ class JsonDeserializer
        private var text: Text
 
        # Root json object parsed from input text.
-       var root: nullable Jsonable is noinit
+       private var root: nullable Jsonable is noinit
 
        # Depth-first path in the serialized object tree.
-       var path = new Array[JsonObject]
+       private var path = new Array[JsonObject]
 
        # Map of references to already deserialized objects.
        private var id_to_object = new StrictHashMap[Int, Object]
@@ -189,10 +292,31 @@ redef class Serializable
        private fun serialize_to_json(v: JsonSerializer)
        do
                var id = v.ref_id_for(self)
-               v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
+               v.stream.write "\{"
+               if not v.plain_json then
+                       v.stream.write "\"__kind\": \"obj\", \"__id\": "
+                       v.stream.write id.to_s
+                       v.stream.write ", \"__class\": \""
+                       v.stream.write class_name
+                       v.stream.write "\""
+               end
                core_serialize_to(v)
                v.stream.write "\}"
        end
+
+       # Serialize this object to plain JSON
+       #
+       # This is a shortcut using `JsonSerializer::plain_json`,
+       # see its documentation for more information.
+       fun to_plain_json: String
+       do
+               var stream = new StringWriter
+               var serializer = new JsonSerializer(stream)
+               serializer.plain_json = true
+               serializer.serialize self
+               stream.close
+               return stream.to_s
+       end
 end
 
 redef class Int
@@ -208,7 +332,16 @@ redef class Bool
 end
 
 redef class Char
-       redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
+       redef fun serialize_to_json(v)
+       do
+               if v.plain_json then
+                       v.stream.write to_s.to_json
+               else
+                       v.stream.write "\{\"__kind\": \"char\", \"__val\": "
+                       v.stream.write to_s.to_json
+                       v.stream.write "\}"
+               end
+       end
 end
 
 redef class String
@@ -242,16 +375,22 @@ redef class SimpleCollection[E]
        redef fun serialize_to_json(v)
        do
                # Register as pseudo object
-               var id = v.ref_id_for(self)
-               v.stream.write """{"__kind": "obj", "__id": """
-               v.stream.write id.to_s
-               v.stream.write """, "__class": """"
-               v.stream.write class_name
-               v.stream.write """", "__length": """
-               v.stream.write length.to_s
-               v.stream.write """, "__items": """
+               if not v.plain_json then
+                       var id = v.ref_id_for(self)
+                       v.stream.write """{"__kind": "obj", "__id": """
+                       v.stream.write id.to_s
+                       v.stream.write """, "__class": """"
+                       v.stream.write class_name
+                       v.stream.write """", "__length": """
+                       v.stream.write length.to_s
+                       v.stream.write """, "__items": """
+               end
+
                serialize_to_pure_json v
-               v.stream.write "\}"
+
+               if not v.plain_json then
+                       v.stream.write "\}"
+               end
        end
 
        redef init from_deserializer(v: Deserializer)
@@ -273,7 +412,7 @@ end
 redef class Array[E]
        redef fun serialize_to_json(v)
        do
-               if class_name == "Array[nullable Serializable]" then
+               if v.plain_json or class_name == "Array[nullable Serializable]" then
                        # Using class_name to get the exact type,
                        # we do not want Array[Int] or anything else here.
 
@@ -288,19 +427,40 @@ redef class Map[K, V]
                # Register as pseudo object
                var id = v.ref_id_for(self)
 
-               v.stream.write """{"__kind": "obj", "__id": """
-               v.stream.write id.to_s
-               v.stream.write """, "__class": """"
-               v.stream.write class_name
-               v.stream.write """", "__length": """
-               v.stream.write length.to_s
-               v.stream.write """, "__keys": """
+               if v.plain_json then
+                       v.stream.write "\{"
+                       var first = true
+                       for key, val in self do
+                               if not first then
+                                       v.stream.write ", "
+                               else first = false
+
+                               if key == null then key = "null"
+
+                               v.stream.write key.to_s.to_json
+                               v.stream.write ": "
+                               if not v.try_to_serialize(val) then
+                                       v.warn("element of type {val.class_name} is not serializable.")
+                                       v.stream.write "null"
+                               end
+                       end
+                       v.stream.write "\}"
+               else
+                       v.stream.write """{"__kind": "obj", "__id": """
+                       v.stream.write id.to_s
+                       v.stream.write """, "__class": """"
+                       v.stream.write class_name
+                       v.stream.write """", "__length": """
+                       v.stream.write length.to_s
 
-               keys.serialize_to_pure_json v
+                       v.stream.write """, "__keys": """
+                       keys.serialize_to_pure_json v
 
-               v.stream.write """, "__values": """
-               values.serialize_to_pure_json v
-               v.stream.write "\}"
+                       v.stream.write """, "__values": """
+                       values.serialize_to_pure_json v
+
+                       v.stream.write "\}"
+               end
        end
 
        # Instantiate a new `Array` from its serialized representation.
@@ -322,59 +482,3 @@ redef class Map[K, V]
                end
        end
 end
-
-# Maps instances to a value, uses `is_same_instance`
-#
-# Warning: This class does not implement all the services from `Map`.
-private class StrictHashMap[K, V]
-       super Map[K, V]
-
-       # private
-       var map = new HashMap[K, Array[Couple[K, V]]]
-
-       redef var length = 0
-
-       redef fun is_empty do return length == 0
-
-       # private
-       fun node_at(key: K): nullable Couple[K, V]
-       do
-               if not map.keys.has(key) then return null
-
-               var arr = map[key]
-               for couple in arr do
-                       if couple.first.is_same_serialized(key) then
-                               return couple
-                       end
-               end
-
-               return null
-       end
-
-       redef fun [](key)
-       do
-               var node = node_at(key)
-               assert node != null
-               return node.second
-       end
-
-       redef fun []=(key, value)
-       do
-               var node = node_at(key)
-               if node != null then
-                       node.second = value
-                       return
-               end
-
-               var arr
-               if not map.keys.has(key) then
-                       arr = new Array[Couple[K, V]]
-                       map[key] = arr
-               else arr = map[key]
-
-               arr.add new Couple[K, V](key, value)
-               self.length += 1
-       end
-
-       redef fun has_key(key) do return node_at(key) != null
-end
index 5743d82..8291616 100644 (file)
@@ -20,7 +20,7 @@ module data_store
 import app::data_store
 private import xdg_basedir
 private import sqlite3
-private import json_serialization
+private import json::serialization
 
 redef class App
        redef var data_store = new LinuxStore
diff --git a/lib/markdown/decorators.nit b/lib/markdown/decorators.nit
new file mode 100644 (file)
index 0000000..1a83394
--- /dev/null
@@ -0,0 +1,186 @@
+# 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.
+
+# Decorators for `markdown` parsing.
+module decorators
+
+import markdown
+
+# `Decorator` that outputs markdown.
+class MdDecorator
+       super Decorator
+
+       redef var headlines = new ArrayMap[String, HeadLine]
+
+       redef fun add_ruler(v, block) do v.add "***\n"
+
+       redef fun add_headline(v, block) do
+               # save headline
+               var txt = block.block.first_line.value
+               var id = strip_id(txt)
+               var lvl = block.depth
+               headlines[id] = new HeadLine(id, txt, lvl)
+               v.add "{"#" * lvl} "
+               v.emit_in block
+               v.addn
+       end
+
+       redef fun add_paragraph(v, block) do
+               v.emit_in block
+               v.addn
+       end
+
+       redef fun add_code(v, block) do
+               if block isa BlockFence and block.meta != null then
+                       v.add "~~~{block.meta.to_s}"
+               else
+                       v.add "~~~"
+               end
+               v.addn
+               v.emit_in block
+               v.add "~~~"
+               v.addn
+       end
+
+       redef fun add_blockquote(v, block) do
+               v.add "> "
+               v.emit_in block
+               v.addn
+       end
+
+       redef fun add_unorderedlist(v, block) do
+               in_unorderedlist = true
+               v.emit_in block
+               in_unorderedlist = false
+       end
+       private var in_unorderedlist = false
+
+       redef fun add_orderedlist(v, block) do
+               in_orderedlist = true
+               current_li = 0
+               v.emit_in block
+               in_unorderedlist = false
+       end
+       private var in_orderedlist = false
+       private var current_li = 0
+
+       redef fun add_listitem(v, block) do
+               if in_unorderedlist then
+                       v.add "* "
+               else if in_orderedlist then
+                       current_li += 1
+                       v.add "{current_li} "
+               end
+               v.emit_in block
+               v.addn
+       end
+
+       redef fun add_em(v, text) do
+               v.add "*"
+               v.add text
+               v.add "*"
+       end
+
+       redef fun add_strong(v, text) do
+               v.add "**"
+               v.add text
+               v.add "**"
+       end
+
+       redef fun add_strike(v, text) do
+               v.add "~~"
+               v.add text
+               v.add "~~"
+       end
+
+       redef fun add_image(v, link, name, comment) do
+               v.add "!["
+               v.add name
+               v.add "]("
+               append_value(v, link)
+               if comment != null and not comment.is_empty then
+                       v.add " "
+                       append_value(v, comment)
+               end
+               v.add ")"
+       end
+
+       redef fun add_link(v, link, name, comment) do
+               v.add "["
+               v.add name
+               v.add "]("
+               append_value(v, link)
+               if comment != null and not comment.is_empty then
+                       v.add " "
+                       append_value(v, comment)
+               end
+               v.add ")"
+       end
+
+       redef fun add_abbr(v, name, comment) do
+               v.add "<abbr title=\""
+               append_value(v, comment)
+               v.add "\">"
+               v.emit_text(name)
+               v.add "</abbr>"
+       end
+
+       redef fun add_span_code(v, text, from, to) do
+               v.add "`"
+               append_code(v, text, from, to)
+               v.add "`"
+       end
+
+       redef fun add_line_break(v) do
+               v.add "\n"
+       end
+
+       redef fun append_value(v, text) do for c in text do escape_char(v, c)
+
+       redef fun escape_char(v, c) do v.addc(c)
+
+       redef fun append_code(v, buffer, from, to) do
+               for i in [from..to[ do
+                       v.addc buffer[i]
+               end
+       end
+
+       redef fun strip_id(txt) do
+               # strip id
+               var b = new FlatBuffer
+               for c in txt do
+                       if c == ' ' then
+                               b.add '_'
+                       else
+                               if not c.is_letter and
+                                  not c.is_digit and
+                                  not allowed_id_chars.has(c) then continue
+                               b.add c
+                       end
+               end
+               var res = b.to_s
+               var key = res
+               # check for multiple id definitions
+               if headlines.has_key(key) then
+                       var i = 1
+                       key = "{res}_{i}"
+                       while headlines.has_key(key) do
+                               i += 1
+                               key = "{res}_{i}"
+                       end
+               end
+               return key
+       end
+
+       private var allowed_id_chars: Array[Char] = ['-', '_', ':', '.']
+end
index 7b7d319..7c65426 100644 (file)
@@ -456,15 +456,23 @@ end
 # The emitter use a `Decorator` to select the output format.
 class MarkdownEmitter
 
+       # Kind of processor used for parsing.
+       type PROCESSOR: MarkdownProcessor
+
        # Processor containing link refs.
-       var processor: MarkdownProcessor
+       var processor: PROCESSOR
+
+       # Kind of decorator used for decoration.
+       type DECORATOR: Decorator
 
        # Decorator used for output.
        # Default is `HTMLDecorator`
-       var decorator: Decorator = new HTMLDecorator is writable
+       var decorator: DECORATOR is writable, lazy do
+               return new HTMLDecorator
+       end
 
        # Create a new `MarkdownEmitter` using a custom `decorator`.
-       init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do
+       init with_decorator(processor: PROCESSOR, decorator: DECORATOR) do
                init processor
                self.decorator = decorator
        end
@@ -481,9 +489,7 @@ class MarkdownEmitter
        fun emit_in(block: Block) do block.emit_in(self)
 
        # Transform and emit mardown text
-       fun emit_text(text: Text) do
-               emit_text_until(text, 0, null)
-       end
+       fun emit_text(text: Text) do emit_text_until(text, 0, null)
 
        # Transform and emit mardown text starting at `from` and
        # until a token with the same type as `token` is found.
@@ -546,10 +552,10 @@ class MarkdownEmitter
        end
 
        # Append `c` to current buffer.
-       fun addc(c: Char) do current_buffer.add c
+       fun addc(c: Char) do add c.to_s
 
        # Append a "\n" line break.
-       fun addn do current_buffer.add '\n'
+       fun addn do add "\n"
 end
 
 # A Link Reference.
@@ -580,64 +586,67 @@ end
 # Default decorator used is `HTMLDecorator`.
 interface Decorator
 
+       # Kind of emitter used for decoration.
+       type EMITTER: MarkdownEmitter
+
        # Render a ruler block.
-       fun add_ruler(v: MarkdownEmitter, block: BlockRuler) is abstract
+       fun add_ruler(v: EMITTER, block: BlockRuler) is abstract
 
        # Render a headline block with corresponding level.
-       fun add_headline(v: MarkdownEmitter, block: BlockHeadline) is abstract
+       fun add_headline(v: EMITTER, block: BlockHeadline) is abstract
 
        # Render a paragraph block.
-       fun add_paragraph(v: MarkdownEmitter, block: BlockParagraph) is abstract
+       fun add_paragraph(v: EMITTER, block: BlockParagraph) is abstract
 
        # Render a code or fence block.
-       fun add_code(v: MarkdownEmitter, block: BlockCode) is abstract
+       fun add_code(v: EMITTER, block: BlockCode) is abstract
 
        # Render a blockquote.
-       fun add_blockquote(v: MarkdownEmitter, block: BlockQuote) is abstract
+       fun add_blockquote(v: EMITTER, block: BlockQuote) is abstract
 
        # Render an unordered list.
-       fun add_unorderedlist(v: MarkdownEmitter, block: BlockUnorderedList) is abstract
+       fun add_unorderedlist(v: EMITTER, block: BlockUnorderedList) is abstract
 
        # Render an ordered list.
-       fun add_orderedlist(v: MarkdownEmitter, block: BlockOrderedList) is abstract
+       fun add_orderedlist(v: EMITTER, block: BlockOrderedList) is abstract
 
        # Render a list item.
-       fun add_listitem(v: MarkdownEmitter, block: BlockListItem) is abstract
+       fun add_listitem(v: EMITTER, block: BlockListItem) is abstract
 
        # Render an emphasis text.
-       fun add_em(v: MarkdownEmitter, text: Text) is abstract
+       fun add_em(v: EMITTER, text: Text) is abstract
 
        # Render a strong text.
-       fun add_strong(v: MarkdownEmitter, text: Text) is abstract
+       fun add_strong(v: EMITTER, text: Text) is abstract
 
        # Render a strike text.
        #
        # Extended mode only (see `MarkdownProcessor::ext_mode`)
-       fun add_strike(v: MarkdownEmitter, text: Text) is abstract
+       fun add_strike(v: EMITTER, text: Text) is abstract
 
        # Render a link.
-       fun add_link(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+       fun add_link(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
 
        # Render an image.
-       fun add_image(v: MarkdownEmitter, link: Text, name: Text, comment: nullable Text) is abstract
+       fun add_image(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
 
        # Render an abbreviation.
-       fun add_abbr(v: MarkdownEmitter, name: Text, comment: Text) is abstract
+       fun add_abbr(v: EMITTER, name: Text, comment: Text) is abstract
 
        # Render a code span reading from a buffer.
-       fun add_span_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+       fun add_span_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
 
        # Render a text and escape it.
-       fun append_value(v: MarkdownEmitter, value: Text) is abstract
+       fun append_value(v: EMITTER, value: Text) is abstract
 
        # Render code text from buffer and escape it.
-       fun append_code(v: MarkdownEmitter, buffer: Text, from, to: Int) is abstract
+       fun append_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
 
        # Render a character escape.
-       fun escape_char(v: MarkdownEmitter, char: Char) is abstract
+       fun escape_char(v: EMITTER, char: Char) is abstract
 
        # Render a line break
-       fun add_line_break(v: MarkdownEmitter) is abstract
+       fun add_line_break(v: EMITTER) is abstract
 
        # Generate a new html valid id from a `String`.
        fun strip_id(txt: String): String is abstract
diff --git a/lib/markdown/wikilinks.nit b/lib/markdown/wikilinks.nit
new file mode 100644 (file)
index 0000000..473bc18
--- /dev/null
@@ -0,0 +1,94 @@
+# 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.
+
+# Wikilinks handling.
+#
+# Wikilinks are on the form `[[link]]`.
+# They can also contain a custom title with the syntax `[[title|link]]`.
+#
+# By importing this module, you enable the `MarkdownProcessor` to recognize
+# `TokenWikiLink` but nothing will happen until you define a
+# `Decorator::add_wikilink` customized to your applciation domain.
+module wikilinks
+
+intrude import markdown
+
+# `MarkdownProcessor` is now able to parse wikilinks.
+redef class MarkdownProcessor
+
+       redef fun token_at(text, pos) do
+               var token = super
+               if not token isa TokenLink then return token
+               if pos + 1 < text.length then
+                       var c = text[pos + 1]
+                       if c == '[' then return new TokenWikiLink(pos, c)
+               end
+               return token
+       end
+end
+
+redef class Decorator
+
+       # Renders a `[[wikilink]]` item.
+       fun add_wikilink(v: EMITTER, link: Text, name, comment: nullable Text) do
+               if name != null then
+                       v.add "[[{name}|{link}]]"
+               else
+                       v.add "[[{link}]]"
+               end
+       end
+end
+
+# A NitiWiki link token.
+#
+# Something of the form `[[foo]]`.
+#
+# Allowed formats:
+#
+# * `[[Wikilink]]`
+# * `[[Wikilink/Bar]]`
+# * `[[Wikilink#foo]]`
+# * `[[Wikilink/Bar#foo]]`
+# * `[[title|Wikilink]]`
+# * `[[title|Wikilink/Bar]]`
+# * `[[title|Wikilink/Bar#foo]]`
+class TokenWikiLink
+       super TokenLink
+
+       redef fun emit_hyper(v) do
+               v.decorator.add_wikilink(v, link.as(not null), name, comment)
+       end
+
+       redef fun check_link(v, out, start, token) do
+               var md = v.current_text
+               var pos = start + 2
+               var tmp = new FlatBuffer
+               pos = md.read_md_link_id(tmp, pos)
+               if pos < start then return -1
+               var name = tmp.write_to_string
+               if name.has("|") then
+                       var parts = name.split_once_on("|")
+                       self.name = parts.first
+                       self.link = parts[1]
+               else
+                       self.name = null
+                       self.link = name
+               end
+               pos += 1
+               pos = md.skip_spaces(pos)
+               if pos < start then return -1
+               pos += 1
+               return pos
+       end
+end
index 5696d32..69d0d9f 100644 (file)
@@ -33,7 +33,7 @@ end
 import c
 intrude import standard::string
 import serialization
-private import json_serialization
+private import json::serialization
 
 in "C Header" `{
        #include <mpi.h>
diff --git a/lib/serialization/engine_tools.nit b/lib/serialization/engine_tools.nit
new file mode 100644 (file)
index 0000000..1e0ee0b
--- /dev/null
@@ -0,0 +1,74 @@
+# 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.
+
+# Advanced services for serialization engines
+module engine_tools
+
+import serialization
+
+# Maps instances to a value, uses `is_same_instance`
+#
+# Warning: This class does not implement all the services from `Map`.
+class StrictHashMap[K, V]
+       super Map[K, V]
+
+       # private
+       var map = new HashMap[K, Array[Couple[K, V]]]
+
+       redef var length = 0
+
+       redef fun is_empty do return length == 0
+
+       # private
+       fun node_at(key: K): nullable Couple[K, V]
+       do
+               if not map.keys.has(key) then return null
+
+               var arr = map[key]
+               for couple in arr do
+                       if couple.first.is_same_serialized(key) then
+                               return couple
+                       end
+               end
+
+               return null
+       end
+
+       redef fun [](key)
+       do
+               var node = node_at(key)
+               assert node != null
+               return node.second
+       end
+
+       redef fun []=(key, value)
+       do
+               var node = node_at(key)
+               if node != null then
+                       node.second = value
+                       return
+               end
+
+               var arr
+               if not map.keys.has(key) then
+                       arr = new Array[Couple[K, V]]
+                       map[key] = arr
+               else arr = map[key]
+
+               arr.add new Couple[K, V](key, value)
+               self.length += 1
+       end
+
+       redef fun has_key(key) do return node_at(key) != null
+end
index 5324f01..1490ee7 100644 (file)
@@ -1024,39 +1024,23 @@ redef class String
        #     assert files.is_empty
        #
        # TODO find a better way to handle errors and to give them back to the user.
-       fun files: Array[String] is extern import Array[String], Array[String].add, NativeString.to_s, String.to_cstring `{
-               char *dir_path;
-               DIR *dir;
-
-               dir_path = String_to_cstring( recv );
-               if ((dir = opendir(dir_path)) == NULL)
-               {
-                       //perror( dir_path );
-                       //exit( 1 );
-                       Array_of_String results;
-                       results = new_Array_of_String();
-                       return results;
-               }
-               else
-               {
-                       Array_of_String results;
-                       String file_name;
-                       struct dirent *de;
-
-                       results = new_Array_of_String();
-
-                       while ( ( de = readdir( dir ) ) != NULL )
-                               if ( strcmp( de->d_name, ".." ) != 0 &&
-                                       strcmp( de->d_name, "." ) != 0 )
-                               {
-                                       file_name = NativeString_to_s( strdup( de->d_name ) );
-                                       Array_of_String_add( results, file_name );
-                               }
+       fun files: Array[String]
+       do
+               var res = new Array[String]
+               var d = new NativeDir.opendir(to_cstring)
+               if d.address_is_null then return res
+
+               loop
+                       var de = d.readdir
+                       if de.address_is_null then break
+                       var name = de.to_s_with_copy
+                       if name == "." or name == ".." then continue
+                       res.add name
+               end
+               d.closedir
 
-                       closedir( dir );
-                       return results;
-               }
-       `}
+               return res
+       end
 end
 
 redef class NativeString
@@ -1129,6 +1113,24 @@ private extern class NativeFile `{ FILE* `}
        new native_stderr is extern "file_NativeFileCapable_NativeFileCapable_native_stderr_0"
 end
 
+# Standard `DIR*` pointer
+private extern class NativeDir `{ DIR* `}
+
+       # Open a directory
+       new opendir(path: NativeString) `{ return opendir(path); `}
+
+       # Close a directory
+       fun closedir `{ closedir(recv); `}
+
+       # Read the next directory entry
+       fun readdir: NativeString `{
+               struct dirent *de;
+               de = readdir(recv);
+               if (!de) return NULL;
+               return de->d_name;
+       `}
+end
+
 redef class Sys
 
        # Standard input
index ede1beb..b2cf479 100644 (file)
@@ -404,6 +404,7 @@ class GlobalCompilerVisitor
        do
                var ret_type = mmodule.native_array_type(elttype)
                ret_type = anchor(ret_type).as(MClassType)
+               length = autobox(length, compiler.mainmodule.int_type)
                return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
        end
 
index 3e7fbcb..085a36d 100644 (file)
@@ -2032,6 +2032,7 @@ class SeparateCompilerVisitor
                self.require_declaration("NEW_{mtype.mclass.c_name}")
                assert mtype isa MGenericType
                var compiler = self.compiler
+               length = autobox(length, compiler.mainmodule.int_type)
                if mtype.need_anchor then
                        hardening_live_open_type(mtype)
                        link_unresolved_type(self.frame.mpropdef.mclassdef, mtype)
index ff7bb48..94f11f2 100644 (file)
@@ -1697,8 +1697,32 @@ class MNullType
        redef fun c_name do return "null"
        redef fun as_nullable do return self
 
-       # Aborts on `null`
-       redef fun as_notnull do abort # sorry...
+       redef var as_notnull = new MBottomType(model) is lazy
+       redef fun need_anchor do return false
+       redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
+       redef fun can_resolve_for(mtype, anchor, mmodule) do return true
+
+       redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef]
+
+       redef fun collect_mclasses(mmodule) do return new HashSet[MClass]
+
+       redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
+end
+
+# The special universal most specific type.
+#
+# This type is intended to be only used internally for type computation or analysis and should not be exposed to the user.
+# The bottom type can de used to denote things that are absurd, dead, or the absence of knowledge.
+#
+# Semantically it is the singleton `null.as_notnull`.
+class MBottomType
+       super MType
+       redef var model: Model
+       redef fun to_s do return "bottom"
+       redef fun full_name do return "bottom"
+       redef fun c_name do return "bottom"
+       redef fun as_nullable do return model.null_type
+       redef fun as_notnull do return self
        redef fun need_anchor do return false
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
        redef fun can_resolve_for(mtype, anchor, mmodule) do return true
diff --git a/tests/sav/nitg-e/test_deserialization_serial.res b/tests/sav/nitg-e/test_deserialization_serial.res
deleted file mode 100644 (file)
index 90e0d60..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-Runtime error: Aborted (../lib/serialization/serialization.nit:109)
-# Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}
-
-# Back in Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
-
-# Back in Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
-
-# Back in Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111         f"\r\/> true>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": {"__kind": "ref", "__id": 0}}
-
-# Back in Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111         f"\r\/> true>
-
-Error: doesn't know how to deserialize class "Array"
index 22ebc32..fc2e797 100644 (file)
@@ -13,7 +13,6 @@ redef class Deserializer
                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)
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
-               if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
                if name == "HashMap[Serializable, Array[Couple[Serializable, Int]]]" then return new HashMap[Serializable, Array[Couple[Serializable, Int]]].from_deserializer(self)
                if name == "Array[Couple[Serializable, Int]]" then return new Array[Couple[Serializable, Int]].from_deserializer(self)
                return super
diff --git a/tests/sav/test_deserialization.res b/tests/sav/test_deserialization.res
deleted file mode 100644 (file)
index 13f9a72..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-Runtime error: Aborted (../lib/serialization/serialization.nit:109)
-# Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}
-
-# Back in Nit:
-<A: true a 0.123 1234 asdf false>
-
-# Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
-
-# Back in Nit:
-<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
-
-# Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"__kind": "obj", "__id": 2, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
-
-# Back in Nit:
-<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
-
-# Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111         f"\r\/> true>
-
-# Json:
-{"__kind": "obj", "__id": 0, "__class": "D", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": {"__kind": "ref", "__id": 0}}
-
-# Back in Nit:
-<D: <B: <A: false b 123.123 2345 new line ->
-<- false> 1111         f"\r\/> true>
-
-Error: doesn't know how to deserialize class "Array[Object]"
diff --git a/tests/sav/test_json_deserialization_alt2.res b/tests/sav/test_json_deserialization_alt2.res
new file mode 100644 (file)
index 0000000..99e49d6
--- /dev/null
@@ -0,0 +1,49 @@
+# Nit:
+<A: true a 0.123 1234 asdf false>
+
+# Json:
+{"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
+
+# Json:
+{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null}}
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false> 1111         f"\r\/> true>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "ii": 1111, "ss": "\tf\"\r\\\/", "d": null}
+
+# Nit:
+<E: a: hello, 1234, 123.4; b: hella, 2345, 234.5>
+
+# Json:
+{"a": ["hello", 1234, 123.4], "b": ["hella", 2345, 234.5]}
+
+# Nit:
+<E: 2222>
+
+# Json:
+{"n": 2222}
+
+# Nit:
+<E: 33.33>
+
+# Json:
+{"n": 33.33}
+
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"hs": [-1, 0], "s": ["one", "two"], "hm": {"one": 1, "two": 2}, "am": {"three": "3", "four": "4"}}
+
index 4fd1ad6..060a759 100644 (file)
@@ -5,3 +5,5 @@ NativeArray[Int]
 3
 1
 1,10,100
+1
+1
diff --git a/tests/sav/test_serialization_alt1.res b/tests/sav/test_serialization_alt1.res
new file mode 100644 (file)
index 0000000..9b381b9
--- /dev/null
@@ -0,0 +1,25 @@
+# Nit:
+<A: true a 0.123 1234 asdf false>
+
+# Json:
+{"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}
+
+# Nit:
+<B: <A: false b 123.123 2345 hjkl false> 1111 qwer>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}
+
+# Nit:
+<C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
+
+# Json:
+{"a": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}, "b": {"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "qwer"}, "aa": {"b": true, "c": "a", "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": [88, "hello", null]}}
+
+# Nit:
+<D: <B: <A: false b 123.123 2345 new line ->
+<- false> 1111         f"\r\/> true>
+
+# Json:
+{"b": false, "c": "b", "f": 123.123, "i": 2345, "s": "new line ->\n<-", "n": null, "array": [88, "hello", null], "ii": 1111, "ss": "\tf\"\r\\\/", "d": null}
+
index f26bcbf..1360bf2 100644 (file)
@@ -15,7 +15,6 @@
 # limitations under the License.
 
 import serialization
-import json_serialization
 
 # Simple class
 class A
@@ -93,7 +92,6 @@ class E
 
        var a = new Array[Object].with_items("hello", 1234, 123.4)
        var b = new Array[nullable Serializable].with_items("hella", 2345, 234.5)
-       init do end
 
        redef fun to_s do return "<E: a: {a.join(", ")}; b: {b.join(", ")}>"
 end
@@ -103,7 +101,6 @@ class F[N: Numeric]
        auto_serializable
 
        var n: N
-       init(n: N) do self.n = n
 
        redef fun to_s do return "<E: {n}>"
 end
@@ -133,31 +130,23 @@ class G
                "hm: {hm.join(", ", ". ")}; am: {am.join(", ", ". ")}>"
 end
 
-var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
-var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
-var c = new C(a, b)
-var d = new D(false, 'b', 123.123, 2345, "new line ->\n<-", null, 1111, "\t\f\"\r\\/")
-d.d = d
-var e = new E
-var fi = new F[Int](2222)
-var ff = new F[Float](33.33)
-var g = new G
-
-# Default works only with Nit serial
-var tests = [a, b, c, d, e, fi, ff, g: Serializable]
-
-# Alt1 should work without nitserial
-#alt1# tests = new Array[Serializable].with_items(a, b, c, d)
-
-for o in tests do
-       var stream = new StringWriter
-       var serializer = new JsonSerializer(stream)
-       serializer.serialize(o)
-
-       var deserializer = new JsonDeserializer(stream.to_s)
-       var deserialized = deserializer.deserialize
-
-       print "# Nit:\n{o}\n"
-       print "# Json:\n{stream}\n"
-       print "# Back in Nit:\n{deserialized or else "null"}\n"
+class TestEntities
+       var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
+       var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
+       var c = new C(a, b)
+       var d = new D(false, 'b', 123.123, 2345, "new line ->\n<-", null, 1111, "\t\f\"\r\\/")
+       init do d.d = d
+       var e = new E
+       var fi = new F[Int](2222)
+       var ff = new F[Float](33.33)
+       var g = new G
+
+       # should work without nitserial
+       var without_generics: Array[Serializable] = [a, b, c, d: Serializable]
+
+       # Default works only with Nit serial
+       var with_generics: Array[Serializable] = [a, b, c, d, e, fi, ff, g: Serializable]
 end
+
+# We instanciate it here so that `nitserial` detects generic types as being alive
+var entities = new TestEntities
index 7d1d443..b0be7fa 100644 (file)
@@ -33,13 +33,7 @@ redef class Deserializer
                if name == "ArrayMap[String, String]" then return new ArrayMap[String, String].from_deserializer(self)
                if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
-               if name == "HashMap[Serializable, Int]" then return new HashMap[Serializable, Int].from_deserializer(self)
-               if name == "Array[JsonObject]" then return new Array[JsonObject].from_deserializer(self)
-               if name == "HashMap[Int, Object]" then return new HashMap[Int, Object].from_deserializer(self)
-               if name == "Array[Node]" then return new Array[Node].from_deserializer(self)
-               if name == "Array[LRState]" then return new Array[LRState].from_deserializer(self)
                if name == "Array[Couple[String, String]]" then return new Array[Couple[String, String]].from_deserializer(self)
-               if name == "Array[nullable Jsonable]" then return new Array[nullable Jsonable].from_deserializer(self)
                return super
        end
 end
diff --git a/tests/test_json_deserialization.nit b/tests/test_json_deserialization.nit
new file mode 100644 (file)
index 0000000..8baadb1
--- /dev/null
@@ -0,0 +1,37 @@
+# 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_deserialization
+import json::serialization
+#alt1# import test_deserialization_serial
+
+var entities = new TestEntities
+
+var tests = entities.without_generics#alt1##alt2#
+#alt1#var tests = entities.with_generics
+#alt2#var tests = entities.with_generics
+
+for o in tests do
+       var stream = new StringWriter
+       var serializer = new JsonSerializer(stream)
+       #alt2#serializer.plain_json = true
+       serializer.serialize(o)
+
+       var deserializer = new JsonDeserializer(stream.to_s)#alt2#
+       var deserialized = deserializer.deserialize#alt2#
+
+       print "# Nit:\n{o}\n"
+       print "# Json:\n{stream}\n"
+       print "# Back in Nit:\n{deserialized or else "null"}\n"#alt2#
+end
index 49ab177..c1ecc49 100644 (file)
@@ -31,3 +31,10 @@ print a.length
 print a[0]
 print a.to_a.join(",")
 
+var i
+i = 3
+a = new NativeArray[Int](i)
+i = 1
+a[i] = i
+print a[i]
+print a[1]
index 1d3de0a..5517cff 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 import serialization
-import json_serialization
+import json::serialization
 
 # Simple class
 class A
@@ -97,6 +97,7 @@ d.d = d
 for o in new Array[Serializable].with_items(a, b, c, d) do
        var stream = new StringWriter
        var serializer = new JsonSerializer(stream)
+       #alt1#serializer.plain_json = true
        serializer.serialize(o)
 
        print "# Nit:\n{o}\n"