Merge: frontend: make the warning on unavailable glslangValidator an advice
authorJean Privat <jean@pryen.org>
Thu, 1 Sep 2016 01:53:17 +0000 (21:53 -0400)
committerJean Privat <jean@pryen.org>
Thu, 1 Sep 2016 01:53:17 +0000 (21:53 -0400)
Changes the following warning to an advice and raise it at each use of the annotations (instead of only once).

~~~
/nit/lib/gamnit/flat.nit:355,9--26: Warning: program `glslangValidator` not in PATH, cannot validate this shader. (glslvalidator)
""" @ glsl_vertex_shader
~~~

Pull-Request: #2299
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

18 files changed:
contrib/mnit_test/src/test_audio.nit
lib/android/audio.nit
lib/app/audio.nit
lib/json/serialization.nit
lib/linux/audio.nit
lib/serialization/serialization.nit
share/nitweb/views/classdef.html
share/nitweb/views/propdef.html
src/frontend/serialization_phase.nit
src/model/model_json.nit
src/modelize/modelize_property.nit
src/semantize/typing.nit
src/web/api_model.nit
src/web/web_base.nit
tests/sav/niti/test_json_deserialization_plain_alt2.res
tests/sav/test_json_deserialization_plain.res
tests/sav/test_json_deserialization_plain_alt2.res
tests/test_json_deserialization_plain.nit

index 9ef5d92..ae94e70 100644 (file)
@@ -22,10 +22,10 @@ import android::audio
 
 redef class App
        # Sound
-       var soundsp: Sound
+       var soundsp: Sound is noinit
 
        # Music
-       var soundmp: Music
+       var soundmp: Music is noinit
 
        # Sound
        var easy_soundsp = new Sound("testsound")
@@ -47,8 +47,8 @@ redef class App
                super
                default_mediaplayer.looping = true
                if test_assets then
-                       soundsp = load_sound("testsound.og")
-                       soundmp = load_music("xylofon.og")
+                       soundsp = new Sound("testsound.og")
+                       soundmp = new Music("xylofon.og")
                        soundmp.play
                end
                if test_ressources then
index e985891..86e019a 100644 (file)
@@ -280,7 +280,7 @@ class SoundPool
                var resval = nsoundpool.load_path(path.to_java_string, priority)
                sys.jni_env.pop_local_frame
                if  resval == -1 then
-                       self.error = new Error("Unable to load sound from path : " + path)
+                       self.error = new Error("Unable to load sound from path: " + path)
                        return new Sound.priv_init(null, -1, self, self.error)
                else
                        return new Sound.priv_init(null, resval, self, null)
@@ -293,12 +293,12 @@ class SoundPool
                return nsoundpool.play(id, left_volume, right_volume, priority, looping, rate)
        end
 
-       # Load a sound by its name in the resources, the sound must be in the `res/raw` folder
-       fun load_name(resource_manager: ResourcesManager, context: NativeActivity, sound: String): Sound do
-               var id = resource_manager.raw_id(sound)
+       # Load a sound by its `name` in the resources, the sound must be in the `res/raw` folder
+       fun load_name(resource_manager: ResourcesManager, context: NativeActivity, name: String): Sound do
+               var id = resource_manager.raw_id(name)
                var resval = nsoundpool.load_id(context, id, priority)
                if  resval == -1 then
-                       self.error = new Error("Unable to load sound from resources : " + sound)
+                       self.error = new Error("Unable to load sound from resources: " + name)
                        return new Sound.priv_init(null, -1, self, self.error)
                else
                        return new Sound.priv_init(null, resval, self, null)
@@ -498,7 +498,7 @@ redef class PlayableAudio
        # Used when the app pause all sounds or resume all sounds
        var paused: Bool = false
 
-       redef init do add_to_sounds(self)
+       redef init do sounds.add self
 end
 
 redef class Sound
@@ -525,16 +525,16 @@ redef class Sound
 
        redef fun load do
                if is_loaded then return
-               var retval_resources = app.default_soundpool.load_name_rid(app.resource_manager, app.native_activity, self.name.strip_extension)
+               var retval_resources = app.default_soundpool.load_name_rid(app.resource_manager, app.native_activity, path.strip_extension)
                if retval_resources == -1 then
-                       self.error = new Error("failed to load" + self.name)
-                       var nam = app.asset_manager.open_fd(self.name)
+                       self.error = new Error("Failed to load " + path)
+                       var nam = app.asset_manager.open_fd(path)
                        if nam.is_java_null then
-                               self.error = new Error("Failed to get file descriptor for " + self.name)
+                               self.error = new Error("Failed to get file descriptor for " + path)
                        else
                                var retval_assets = app.default_soundpool.load_asset_fd_rid(nam)
                                if retval_assets == -1 then
-                                       self.error = new Error("Failed to load" + self.name)
+                                       self.error = new Error("Failed to load " + path)
                                else
                                        self.soundpool_id = retval_assets
                                        self.soundpool = app.default_soundpool
@@ -549,6 +549,9 @@ redef class Sound
                        self.soundpool.error = null
                end
                is_loaded = true
+
+               var error = error
+               if error != null then print_error error
        end
 
        redef fun play do
@@ -591,12 +594,12 @@ redef class Music
 
        redef fun load do
                if is_loaded then return
-               var mp_sound_resources = app.default_mediaplayer.load_sound(app.resource_manager.raw_id(self.name.strip_extension), app.native_activity)
+               var mp_sound_resources = app.default_mediaplayer.load_sound(app.resource_manager.raw_id(path.strip_extension), app.native_activity)
                if mp_sound_resources.error != null then
                        self.error = mp_sound_resources.error
-                       var nam = app.asset_manager.open_fd(self.name)
+                       var nam = app.asset_manager.open_fd(path)
                        if nam.is_java_null then
-                               self.error = new Error("Failed to get file descriptor for " + self.name)
+                               self.error = new Error("Failed to get file descriptor for " + path)
                        else
                                var mp_sound_assets = app.default_mediaplayer.data_source_fd(nam)
                                if mp_sound_assets.error != null then
@@ -613,6 +616,9 @@ redef class Music
                        self.media_player.error = null
                end
                is_loaded = true
+
+               var error = error
+               if error != null then print_error error
        end
 
        redef fun play do
@@ -658,38 +664,18 @@ redef class App
                App_native_activity(self).setVolumeControlStream(AudioManager.STREAM_MUSIC);
        `}
 
-       # Retrieves a sound with a soundpool in the `assets` folder using its name.
-       # Used to play short songs, can play multiple sounds simultaneously
-       redef fun load_sound(path) do
-               var fd = asset_manager.open_fd(path)
-               if not fd.is_java_null then
-                       return add_to_sounds(default_soundpool.load_asset_fd(fd)).as(Sound)
-               else
-                       var error = new Error("Failed to load Sound {path}")
-                       return new Sound.priv_init(null, -1, default_soundpool, error)
-               end
-       end
-
-       # Retrieves a music with a media player in the `assets` folder using its name.
-       # Used to play long sounds or musics, can't play multiple sounds simultaneously
-       redef fun load_music(path) do
-               var fd = asset_manager.open_fd(path)
-               if not fd.is_java_null then
-                       return add_to_sounds(default_mediaplayer.data_source_fd(fd)).as(Music)
-               else
-                       var error = new Error("Failed to load music {path}")
-                       return new Music.priv_init(null, default_mediaplayer, error)
-               end
-       end
-
        # Same as `load_sound` but load the sound from the `res/raw` folder
        fun load_sound_from_res(sound_name: String): Sound do
-               return add_to_sounds(default_soundpool.load_name(resource_manager,self.native_activity, sound_name)).as(Sound)
+               var sound = default_soundpool.load_name(resource_manager,self.native_activity, sound_name)
+               sys.sounds.add sound
+               return sound
        end
 
        # Same as `load_music` but load the sound from the `res/raw` folder
        fun load_music_from_res(music: String): Music do
-               return add_to_sounds(default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)).as(Music)
+               var sound = default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)
+               sys.sounds.add sound
+               return sound
        end
 
        redef fun on_pause do
@@ -728,10 +714,4 @@ redef class Sys
        # Sounds handled by the application, when you load a sound, it's added to this list.
        # This array is used in `pause` and `resume`
        private var sounds = new Array[PlayableAudio]
-
-       # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res`
-       private fun add_to_sounds(sound: PlayableAudio): PlayableAudio do
-               sounds.add(sound)
-               return sound
-       end
 end
index 7f22c78..17310d8 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# App audio abstraction
-# Default behaviour is loading the audio from the `assets` folder of the project with its name and extension
-# Platforms implementations can modify this comportement
+# Services to load and play `Sound` and `Music` from the assets folder
 #
-# Once the application has started (after `App.setup`)
-# use `App.load_sound` to get a sound
-# then `Sound.play` to play it
+# Get a handle to a sound using `new Sound` or `new Music` at any time.
+# Call `load` at or after `App::on_create` or leave it to be loaded
+# on demand by the first call to `play`.
 module audio
 
 import app_base
@@ -33,43 +31,34 @@ import android::audio is conditional(android)
 # Abstraction of a playable Audio
 abstract class PlayableAudio
 
-       # Name of this playable audio
-       var name: String
+       # Path to the audio file in the assets folder
+       var path: String
 
-       # Error which is not null if an error happened
+       # Last error on this sound, if any
        var error: nullable Error = null is writable
 
-       # Is this already loaded ?
+       # Is `self` already loaded?
        protected var is_loaded = false is writable
 
        # Load this playable audio
        fun load is abstract
 
-       # Plays the sound
+       # Play the sound
        fun play is abstract
 
-       # Pauses the sound
+       # Pause the sound
        fun pause is abstract
 
-       # Resumes the sound
+       # Resume the sound
        fun resume is abstract
 end
 
-# Abstraction of a short sound
+# Short sound
 class Sound
        super PlayableAudio
 end
 
-# Abstraction of a long song, that can bee looped
+# Long sound that can bee looped
 class Music
        super PlayableAudio
 end
-
-redef class App
-
-       # Load a sound
-       fun load_sound(name: String): Sound is abstract
-
-       # Load a music
-       fun load_music(name: String): Music is abstract
-end
index e26b8ec..983f50b 100644 (file)
 # assert deserializer.errors.is_empty # If false, `obj` is invalid
 # print object
 # ~~~
+#
+# ### Missing attributes and default values
+#
+# When reading JSON, some attributes expected by Nit classes may be missing.
+# The JSON object may come from an external API using optional attributes or
+# from a previous version of your program without the attributes.
+# When an attribute is not found, the deserialization engine acts in one of three ways:
+#
+# 1. If the attribute has a default value or if it is annotated by `lazy`,
+#    the engine leave the attribute to the default value. No error is raised.
+# 2. If the static type of the attribute is nullable, the engine sets
+#    the attribute to `null`. No error is raised.
+# 3. Otherwise, the engine raises an error and does not set the attribute.
+#    The caller must check for `errors` and must not read from the attribute.
+#
+# ~~~nitish
+# import json::serialization
+#
+# class MyConfig
+#     serialize
+#
+#     var width: Int # Must be in JSON or an error is raised
+#     var height = 4
+#     var volume_level = 8 is lazy
+#     var player_name: nullable String
+#     var tmp_dir: nullable String = "/tmp" is lazy
+# end
+#
+# # ---
+# # JSON object with all expected attributes -> OK
+# var plain_json = """
+# {
+#     "width": 11,
+#     "height": 22,
+#     "volume_level": 33,
+#     "player_name": "Alice",
+#     "tmp_dir": null
+# }"""
+# var deserializer = new JsonDeserializer(plain_json)
+# var obj = new MyConfig.from_deserializer(deserializer)
+#
+# assert deserializer.errors.is_empty
+# assert obj.width == 11
+# assert obj.height == 22
+# assert obj.volume_level == 33
+# assert obj.player_name == "Alice"
+# assert obj.tmp_dir == null
+#
+# # ---
+# # JSON object missing optional attributes -> OK
+# plain_json = """
+# {
+#     "width": 11
+# }"""
+# deserializer = new JsonDeserializer(plain_json)
+# obj = new MyConfig.from_deserializer(deserializer)
+#
+# assert deserializer.errors.is_empty
+# assert obj.width == 11
+# assert obj.height == 4
+# assert obj.volume_level == 8
+# assert obj.player_name == null
+# assert obj.tmp_dir == "/tmp"
+#
+# # ---
+# # JSON object missing the mandatory attribute -> Error
+# plain_json = """
+# {
+#     "player_name": "Bob",
+# }"""
+# deserializer = new JsonDeserializer(plain_json)
+# obj = new MyConfig.from_deserializer(deserializer)
+#
+# # There's an error, `obj` is partial
+# assert deserializer.errors.length == 1
+#
+# # Still, we can access valid attributes
+# assert obj.player_name == "Bob"
+# ~~~
 module serialization
 
 import ::serialization::caching
@@ -295,13 +374,15 @@ class JsonDeserializer
                        if not root isa Error then
                                errors.add new Error("Deserialization Error: parsed JSON value is not an object.")
                        end
+                       deserialize_attribute_missing = false
                        return null
                end
 
                var current = path.last
 
                if not current.keys.has(name) then
-                       errors.add new Error("Deserialization Error: JSON object has not attribute '{name}'.")
+                       # Let the generated code / caller of `deserialize_attribute` raise the missing attribute error
+                       deserialize_attribute_missing = true
                        return null
                end
 
@@ -310,6 +391,8 @@ class JsonDeserializer
                attributes_path.add name
                var res = convert_object(value, static_type)
                attributes_path.pop
+
+               deserialize_attribute_missing = false
                return res
        end
 
index 297c7e9..09ddfff 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Linux audio services
+# Linux audio implementation
 module audio
 
 import app::audio
 import linux
 
-# Simple audio asset
 redef class Sound
 
        redef fun play do
-               if name.has_suffix(".wav") then
-                       sys.system "aplay -q {app.assets_dir}{name} &"
-               else if name.has_suffix(".mp3") then
-                       sys.system "mpg123 -q {app.assets_dir}{name} &"
+               if path.has_suffix(".wav") then
+                       sys.system "aplay -q {app.assets_dir}{path} &"
+               else if path.has_suffix(".mp3") then
+                       sys.system "mpg123 -q {app.assets_dir}{path} &"
                end
        end
 
@@ -39,10 +38,10 @@ end
 redef class Music
 
        redef fun play do
-               if name.has_suffix(".wav") then
-                       sys.system "aplay -q {app.assets_dir}{name} &"
-               else if name.has_suffix(".mp3") then
-                       sys.system "mpg123 -q {app.assets_dir}{name} &"
+               if path.has_suffix(".wav") then
+                       sys.system "aplay -q {app.assets_dir}{path} &"
+               else if path.has_suffix(".mp3") then
+                       sys.system "mpg123 -q {app.assets_dir}{path} &"
                end
        end
 
@@ -50,15 +49,3 @@ redef class Music
        redef fun pause do end
        redef fun resume do end
 end
-
-redef class App
-       redef fun load_sound(name)
-       do
-               return new Sound(name)
-       end
-
-       redef fun load_music(name)
-       do
-               return new Music(name)
-       end
-end
index 5630b34..db02122 100644 (file)
@@ -100,9 +100,15 @@ abstract class Deserializer
        # The `static_type` can be used as last resort if the deserialized object
        # desn't have any metadata declaring the dynamic type.
        #
+       # Return the deserialized value or null on error, and set
+       # `deserialize_attribute_missing` to whether the attribute was missing.
+       #
        # Internal method to be implemented by the engines.
        fun deserialize_attribute(name: String, static_type: nullable String): nullable Object is abstract
 
+       # Was the attribute queried by the last call to `deserialize_attribute` missing?
+       var deserialize_attribute_missing = false
+
        # Register a newly allocated object (even if not completely built)
        #
        # Internal method called by objects in creation, to be implemented by the engines.
index f44478f..04dfa46 100644 (file)
@@ -1,4 +1,4 @@
-<ul class='nav nav-tabs' role='tablist'>
+<ul class='nav nav-tabs' ng-init='entityCtrl.loadEntityLinearization()'>
        <li role='presentation' class='warning'>
                <a ng-href='{{mentity.mclass.web_url}}'>
                        <span class='glyphicon glyphicon-chevron-left'/> Go to class
index 014e820..825a782 100644 (file)
@@ -1,4 +1,4 @@
-<ul class='nav nav-tabs'>
+<ul class='nav nav-tabs' ng-init='entityCtrl.loadEntityLinearization()'>
        <li role='presentation' class='warning'>
                <a href='{{mentity.mproperty.web_url}}'>
                        <span class='glyphicon glyphicon-chevron-left'/> Go to property
index b9cab9d..9eb01a4 100644 (file)
@@ -312,18 +312,29 @@ do
                                code.add """
        self.{{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", "{{{type_name}}}")
 """
-                       else code.add """
+                       else
+                               code.add """
        var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", "{{{type_name}}}")
-       if not {{{name}}} isa {{{type_name}}} then
-               # Check if it was a subjectent error
-               v.errors.add new AttributeTypeError(self, "{{{attribute.serialize_name}}}", {{{name}}}, "{{{type_name}}}")
+       if v.deserialize_attribute_missing then
+"""
+                               # What to do when an attribute is missing?
+                               if attribute.has_value then
+                                       # Leave it to the default value
+                               else if mtype isa MNullableType then
+                                       code.add """
+               self.{{{name}}} = null"""
+                               else code.add """
+               v.errors.add new Error("Deserialization Error: attribute `{class_name}::{{{name}}}` missing from JSON object")"""
 
-               # Clear subjacent error
+                               code.add """
+       else if not {{{name}}} isa {{{type_name}}} then
+               v.errors.add new AttributeTypeError(self, "{{{attribute.serialize_name}}}", {{{name}}}, "{{{type_name}}}")
                if v.keep_going == false then return
        else
                self.{{{name}}} = {{{name}}}
        end
 """
+                       end
                end
 
                code.add "end"
index 658e73d..2c5cd04 100644 (file)
@@ -51,8 +51,8 @@ redef class MEntity
 
        # Return `self` as a JsonObject.
        #
-       # By default, every reference to another MEntity is replaced by a pointer
-       # to the MEntity::json_id.
+       # By default, every reference to another MEntity is skipped.
+       # Use full_json for a full representation.
        fun json: JsonObject do
                var obj = new JsonObject
                obj["name"] = name
@@ -60,7 +60,6 @@ redef class MEntity
                obj["full_name"] = full_name
                obj["mdoc"] = mdoc_or_fallback
                obj["visibility"] = visibility
-               obj["location"] = location
                var modifiers = new JsonArray
                for modifier in collect_modifiers do
                        modifiers.add modifier
@@ -70,6 +69,19 @@ redef class MEntity
        end
 
        redef fun to_json do return json.to_json
+
+       # Return `self` as a JsonObject with references.
+       #
+       # By default, every reference to another MEntity is replaced by a pointer
+       # to the MEntity::json_id.
+       fun full_json: JsonObject do
+               var obj = json
+               obj["location"] = location
+               return obj
+       end
+
+       # Return `full_json` as a json string.
+       fun to_full_json: String do return full_json.to_json
 end
 
 redef class MDoc
@@ -114,7 +126,7 @@ end
 
 redef class MPackage
 
-       redef fun json do
+       redef fun full_json do
                var obj = super
                if ini != null then
                        obj["ini"] = new JsonObject.from(ini.as(not null).to_map)
@@ -129,6 +141,11 @@ redef class MGroup
        redef fun json do
                var obj = super
                obj["is_root"] = is_root
+               return obj
+       end
+
+       redef fun full_json do
+               var obj = super
                obj["mpackage"] = to_mentity_ref(mpackage)
                obj["default_mmodule"] = to_mentity_ref(default_mmodule)
                obj["parent"] = to_mentity_ref(parent)
@@ -139,12 +156,15 @@ redef class MGroup
 end
 
 redef class MModule
-       redef fun json do
+       redef fun full_json do
                var obj = super
                obj["mpackage"] = to_mentity_ref(mpackage)
                obj["mgroup"] = to_mentity_ref(mgroup)
                obj["intro_mclasses"] = to_mentity_refs(intro_mclasses)
                obj["mclassdefs"] = to_mentity_refs(mclassdefs)
+               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
+               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
+               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
                return obj
        end
 end
@@ -152,13 +172,20 @@ end
 redef class MClass
        redef fun json do
                var obj = super
-               var arr = new JsonArray
-               for mparameter in mparameters do arr.add mparameter
-               obj["mparameters"] = arr
+               obj["mparameters"] = new JsonArray.from(mparameters)
+               return obj
+       end
+
+       redef fun full_json do
+               var obj = super
                obj["intro"] = to_mentity_ref(intro)
                obj["intro_mmodule"] = to_mentity_ref(intro_mmodule)
                obj["mpackage"] = to_mentity_ref(intro_mmodule.mpackage)
                obj["mclassdefs"] = to_mentity_refs(mclassdefs)
+               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
+               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
+               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
+               obj["parents"] = to_mentity_refs(collect_parents(private_view))
                return obj
        end
 end
@@ -167,23 +194,32 @@ redef class MClassDef
        redef fun json do
                var obj = super
                obj["is_intro"] = is_intro
-               var arr = new JsonArray
-               for mparameter in mclass.mparameters do arr.add mparameter
-               obj["mparameters"] = arr
+               obj["mparameters"] = new JsonArray.from(mclass.mparameters)
+               return obj
+       end
+
+       redef fun full_json do
+               var obj = super
                obj["mmodule"] = to_mentity_ref(mmodule)
                obj["mclass"] = to_mentity_ref(mclass)
                obj["mpropdefs"] = to_mentity_refs(mpropdefs)
                obj["intro_mproperties"] = to_mentity_refs(intro_mproperties)
+               obj["intro"] = to_mentity_ref(mclass.intro)
+               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
+               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
+               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
                return obj
        end
 end
 
 redef class MProperty
-       redef fun json do
+       redef fun full_json do
                var obj = super
                obj["intro"] = to_mentity_ref(intro)
                obj["intro_mclassdef"] = to_mentity_ref(intro_mclassdef)
                obj["mpropdefs"] = to_mentity_refs(mpropdefs)
+               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
+               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
                return obj
        end
 end
@@ -218,8 +254,18 @@ redef class MPropDef
        redef fun json do
                var obj = super
                obj["is_intro"] = is_intro
+               return obj
+       end
+
+       redef fun full_json do
+               var obj = super
                obj["mclassdef"] = to_mentity_ref(mclassdef)
                obj["mproperty"] = to_mentity_ref(mproperty)
+               obj["intro"] = to_mentity_ref(mproperty.intro)
+               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
+               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
+               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
+               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
                return obj
        end
 end
index 9465dbb..44d551d 100644 (file)
@@ -1158,8 +1158,8 @@ redef class AAttrPropdef
        # Is the node tagged optional?
        var is_optional = false
 
-       # Has the node a default value?
-       # Could be through `n_expr` or `n_block`
+       # Does the node have a default value?
+       # Could be through `n_expr`, `n_block` or `is_lazy`
        var has_value = false
 
        # The guard associated to a lazy attribute.
index bec49a8..9e2d69a 100644 (file)
@@ -102,9 +102,9 @@ private class TypeVisitor
        end
 
        # Check that `sub` is a subtype of `sup`.
-       # If `sub` is not a valid suptype, then display an error on `node` an return null.
-       # If `sub` is a safe subtype of `sup` then return `sub`.
-       # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`.
+       # If `sub` is not a valid suptype, then display an error on `node` and return `null`.
+       # If `sub` is a safe subtype of `sup`, then return `sub`.
+       # If `sub` is an unsafe subtype (i.e., an implicit cast is required), then return `sup`.
        #
        # The point of the return type is to determinate the usable type on an expression when `autocast` is true:
        # If the suptype is safe, then the return type is the one on the expression typed by `sub`.
index 4c9fb8e..b6ac745 100644 (file)
@@ -162,7 +162,9 @@ class APIEntityLinearization
                        res.api_error(404, "No linearization for mentity `{mentity.full_name}`")
                        return
                end
-               res.json new JsonArray.from(lin)
+               var mentities = new JsonArray
+               for e in lin do mentities.add e.full_json
+               res.json mentities
        end
 end
 
index 043bd6c..9b29207 100644 (file)
@@ -143,7 +143,7 @@ redef class MEntity
        end
 
        # Get the full json repesentation of `self` with MEntityRefs resolved.
-       fun api_json(handler: APIHandler): JsonObject do return json
+       fun api_json(handler: APIHandler): JsonObject do return full_json
 end
 
 redef class MEntityRef
@@ -154,7 +154,6 @@ redef class MEntityRef
                obj["name"] = mentity.name
                obj["mdoc"] = mentity.mdoc_or_fallback
                obj["visibility"] = mentity.visibility
-               obj["location"] = mentity.location
                var modifiers = new JsonArray
                for modifier in mentity.collect_modifiers do
                        modifiers.add modifier
@@ -172,77 +171,21 @@ redef class MEntityRef
                end
                return obj
        end
-end
-
-redef class MDoc
-
-       # Add doc down processing
-       redef fun json do
-               var obj = super
-               obj["synopsis"] = synopsis
-               obj["documentation"] = documentation
-               obj["comment"] = comment
-               obj["html_synopsis"] = html_synopsis.write_to_string
-               obj["html_documentation"] = html_documentation.write_to_string
-               obj["html_comment"] = html_comment.write_to_string
-               return obj
-       end
-end
-
-redef class MModule
-       redef fun api_json(handler) do
-               var obj = super
-               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
-               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
-               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
-               return obj
-       end
-end
-
-redef class MClass
-       redef fun api_json(handler) do
-               var obj = super
-               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
-               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
-               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
-               obj["parents"] = to_mentity_refs(collect_parents(private_view))
-               return obj
-       end
-end
-
-redef class MClassDef
-       redef fun json do
-               var obj = super
-               obj["intro"] = to_mentity_ref(mclass.intro)
-               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
-               return obj
-       end
 
-       redef fun api_json(handler) do
+       redef fun full_json do
                var obj = super
-               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
-               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
+               obj["location"] = mentity.location
                return obj
        end
 end
 
-redef class MProperty
-       redef fun json do
-               var obj = super
-               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
-               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
-               return obj
-       end
-end
+redef class MDoc
 
-redef class MPropDef
+       # Add doc down processing
        redef fun json do
-               var obj = super
-               obj["intro"] = to_mentity_ref(mproperty.intro)
-               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
-               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
-               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
-               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
+               var obj = new JsonObject
+               obj["html_synopsis"] = html_synopsis.write_to_string
+               obj["html_documentation"] = html_documentation.write_to_string
                return obj
        end
 end
index 102d57b..a6ae660 100644 (file)
@@ -1,3 +1,3 @@
 Runtime error: Uninitialized attribute _s (alt/test_json_deserialization_plain_alt2.nit:27)
 # JSON: {"__class": "MyClass", "i": 123, "o": null}
-# Errors: 'Deserialization Error: JSON object has not attribute 's'.', 'Deserialization Error: Wrong type on `MyClass::s` expected `String`, got `null`', 'Deserialization Error: JSON object has not attribute 'f'.', 'Deserialization Error: Wrong type on `MyClass::f` expected `Float`, got `null`', 'Deserialization Error: JSON object has not attribute 'a'.', 'Deserialization Error: Wrong type on `MyClass::a` expected `Array[String]`, got `null`'
+# Errors: 'Deserialization Error: attribute `MyClass::s` missing from JSON object', 'Deserialization Error: attribute `MyClass::f` missing from JSON object', 'Deserialization Error: attribute `MyClass::a` missing from JSON object'
index 71dc9e7..62b3c82 100644 (file)
@@ -11,7 +11,6 @@
 # Nit: <MyClass i:123 s:hello f:123.456 a:[one, two] o:<null>>
 
 # JSON: {"__class": "MyClass", "i": 123, "s": "hello", "f": 123.456, "a": ["one", "two"]}
-# Errors: 'Deserialization Error: JSON object has not attribute 'o'.'
 # Nit: <MyClass i:123 s:hello f:123.456 a:[one, two] o:<null>>
 
 # JSON: {"__class": "MyClass", "i": 123, "s": "hello", "f": 123.456, "a": ["one", "two"], "o":
index 54e5a4d..4b53f0f 100644 (file)
@@ -1,3 +1,3 @@
 Runtime error: Uninitialized attribute _s (alt/test_json_deserialization_plain_alt2.nit:22)
 # JSON: {"__class": "MyClass", "i": 123, "o": null}
-# Errors: 'Deserialization Error: JSON object has not attribute 's'.', 'Deserialization Error: Wrong type on `MyClass::s` expected `String`, got `null`', 'Deserialization Error: JSON object has not attribute 'f'.', 'Deserialization Error: Wrong type on `MyClass::f` expected `Float`, got `null`', 'Deserialization Error: JSON object has not attribute 'a'.', 'Deserialization Error: Wrong type on `MyClass::a` expected `Array[String]`, got `null`'
+# Errors: 'Deserialization Error: attribute `MyClass::s` missing from JSON object', 'Deserialization Error: attribute `MyClass::f` missing from JSON object', 'Deserialization Error: attribute `MyClass::a` missing from JSON object'
index e577eda..7358c85 100644 (file)
@@ -49,7 +49,7 @@ tests.add """
 tests.add """
 {"__class": "MyClass", "i": 123, "s": "hello", "f": 123.456, "o": null, "a": ["one", "two"], "Some random attribute": 777}"""
 
-# Skipping `o` will cause an error but the attribute will be set to `null`
+# Skipping `o` will set the attribute to `null`
 tests.add """
 {"__class": "MyClass", "i": 123, "s": "hello", "f": 123.456, "a": ["one", "two"]}"""