Adds a missing features to the Java FFI: pinning references to Nit objects from Java. Both services, `..._incr_ref` and `..._decr_ref`, are "simple" redirections from Java to the nitni service in C through the JNI.
Pull-Request: #1820
Reviewed-by: Jean Privat <jean@pryen.org>
#ifdef ANDROID
#include <android/log.h>
#define PRINT_ERROR(...) ((void)__android_log_print(ANDROID_LOG_WARN, "nit", __VA_ARGS__))
+
+ // FIXME bring back when the GC is fixed in Android
+ #undef WITH_LIBGC
#else
#define PRINT_ERROR(...) ((void)fprintf(stderr, __VA_ARGS__))
#endif
tags=lib
maintainer=Jean Privat <jean@pryen.org>
license=Apache-2.0
+more_contributors=Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>, Romain Chanoir <romain.chanoir@viacesi.fr>, Christophe Gigax <christophe.gigax@viacesi.fr>, Frédéric Vachon <fredvac@gmail.com>, Jean-Sebastien Gelinas <calestar@gmail.com>, Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>, Johan Kayser <johan.kayser@viacesi.fr>, Johann Dubois <johann.dubois@outlook.com>, Julien Pagès <julien.projet@gmail.com>
[upstream]
browse=https://github.com/nitlang/nit/tree/master/lib/core
git=https://github.com/nitlang/nit.git
return res.to_s
end
+ # Returns `self` with all characters escaped with their UTF-16 representation
+ #
+ # assert "Aèあ𐏓".escape_to_utf16 == "\\u0041\\u00e8\\u3042\\ud800\\udfd3"
+ fun escape_to_utf16: String do
+ var buf = new Buffer
+ for i in chars do buf.append i.escape_to_utf16
+ return buf.to_s
+ end
+
+ # Returns the Unicode char escaped by `self`
+ #
+ # assert "\\u0041".from_utf16_escape == 'A'
+ # assert "\\ud800\\udfd3".from_utf16_escape == '𐏓'
+ # assert "\\u00e8".from_utf16_escape == 'è'
+ # assert "\\u3042".from_utf16_escape == 'あ'
+ fun from_utf16_escape: Char do
+ var ln = length
+ if ln != 6 and ln != 12 then return 0xFFFD.code_point
+ var cphi = substring(2, 4).to_hex
+ if cphi < 0xD800 then return cphi.code_point
+ if cphi > 0xDFFF then return cphi.code_point
+ if cphi > 0xDBFF then return 0xFFFD.code_point
+ var cp = 0
+ cp += (cphi - 0xD800) << 10
+ var cplo = substring(8, 4).to_hex
+ if cplo < 0xDC00 then return 0xFFFD.code_point
+ if cplo > 0xDFFF then return 0xFFFD.code_point
+ cp += cplo - 0xDC00
+ cp += 0x10000
+ return cp.code_point
+ end
+
# Encode `self` to percent (or URL) encoding
#
# assert "aBc09-._~".to_percent_encoding == "aBc09-._~"
return ns.to_s_with_length(ln)
end
+ # Returns `self` escaped to UTF-16
+ #
+ # i.e. Represents `self`.`code_point` using UTF-16 codets escaped
+ # with a `\u`
+ #
+ # assert 'A'.escape_to_utf16 == "\\u0041"
+ # assert 'è'.escape_to_utf16 == "\\u00e8"
+ # assert 'あ'.escape_to_utf16 == "\\u3042"
+ # assert '𐏓'.escape_to_utf16 == "\\ud800\\udfd3"
+ fun escape_to_utf16: String do
+ var cp = code_point
+ var buf: Buffer
+ if cp < 0xD800 or (cp >= 0xE000 and cp <= 0xFFFF) then
+ buf = new Buffer.with_cap(6)
+ buf.append("\\u0000")
+ var hx = cp.to_hex
+ var outid = 5
+ for i in hx.chars.reverse_iterator do
+ buf[outid] = i
+ outid -= 1
+ end
+ else
+ buf = new Buffer.with_cap(12)
+ buf.append("\\u0000\\u0000")
+ var lo = (((cp - 0x10000) & 0x3FF) + 0xDC00).to_hex
+ var hi = ((((cp - 0x10000) & 0xFFC00) >> 10) + 0xD800).to_hex
+ var out = 2
+ for i in hi do
+ buf[out] = i
+ out += 1
+ end
+ out = 8
+ for i in lo do
+ buf[out] = i
+ out += 1
+ end
+ end
+ return buf.to_s
+ end
+
private fun u8char_tos(r: NativeString, len: Int) `{
r[len] = '\0';
switch(len){
assert count >= 0
if from < 0 then from = 0
if (from + count) > length then count = length - from
- if count != 0 then
- var its = _items
- var bytefrom = its.char_to_byte_index(from)
- var byteto = its.char_to_byte_index(count + from - 1)
- byteto += its.char_at(byteto).u8char_len - 1
- var byte_length = byteto - bytefrom + 1
- var r_items = new NativeString(byte_length)
- its.copy_to(r_items, byte_length, bytefrom, 0)
- return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
- else
- return new Buffer
- end
+ if count <= 0 then return new Buffer
+ var its = _items
+ var bytefrom = its.char_to_byte_index(from)
+ var byteto = its.char_to_byte_index(count + from - 1)
+ byteto += its.char_at(byteto).u8char_len - 1
+ var byte_length = byteto - bytefrom + 1
+ var r_items = new NativeString(byte_length)
+ its.copy_to(r_items, byte_length, bytefrom, 0)
+ return new FlatBuffer.with_infos(r_items, byte_length, byte_length, count)
end
redef fun reverse
redef fun add(c) do
var rp = rpos
- if rp >= buf_size then
+ var remsp = buf_size - rp
+ var cln = c.u8char_len
+ if cln > remsp then
dump_buffer
rp = 0
end
- # TODO: Fix when supporting UTF-8
- ns[rp] = c.ascii
- rp += 1
- _bytelen += 1
+ ns.set_char_at(rp, c)
+ rp += cln
+ _bytelen += cln
rpos = rp
end
redef fun next do
index -= 1
- if pns > 0 then
+ if pns >= 0 then
pns -= 1
else
sit.next
protected fun to_json_by_append: String do
var buffer = new RopeBuffer
append_json(buffer)
- return buffer.write_to_string
+ return buffer.to_s
end
# Append the JSON representation of `self` to the specified buffer.
var res = new FlatBuffer
pretty_json_visit(res, 0)
res.add '\n'
- return res.write_to_string
+ return res.to_s
end
private fun pretty_json_visit(buffer: FlatBuffer, indent: Int) is abstract
redef fun append_json(buffer) do
buffer.add '\"'
- for i in [0..self.length[ do
+ for i in [0 .. self.length[ do
var char = self[i]
if char == '\\' then
buffer.append "\\\\"
buffer.append "\\\""
else if char == '\/' then
buffer.append "\\/"
- else if char < 16.code_point then
+ else if char < ' ' then
if char == '\n' then
buffer.append "\\n"
else if char == '\r' then
else if char == 0x08.code_point then
buffer.append "\\b"
else
- buffer.append "\\u000{char.code_point.to_hex}"
+ buffer.append char.escape_to_utf16
end
- else if char < ' ' then
- buffer.append "\\u00{char.code_point.to_hex}"
else
buffer.add char
end
redef class Nstring
# The represented string.
private fun to_nit_string: String do
- var res = new FlatBuffer
+ var res = new Buffer
var i = 1
while i < text.length - 1 do
var char = text[i]
else if char == 't' then
char = '\t'
else if char == 'u' then
- var code = text.substring(i + 1, 4).to_hex
- # TODO UTF-16 escaping is not supported yet.
- if code >= 128 then
- char = '?'
- else
- char = code.code_point
+ var escape = new Buffer
+ escape.append "\\u"
+ var code = text.substring(i + 1, 4)
+ escape.append code
+ var hx = code.to_hex
+ if hx >= 0xD800 and hx <= 0xDFFF then
+ var lostr = text.substring(i + 7, 4)
+ if lostr.length < 4 then
+ escape.clear
+ escape.append "\\uFFFD"
+ else
+ escape.append "\\u"
+ escape.append lostr
+ end
+ i += 6
end
i += 4
+ char = escape.from_utf16_escape
end
# `"`, `/` or `\` => Keep `char` as-is.
end
res.add char
i += 1
end
- return res.write_to_string
+ return res.to_s
end
end
redef fun write_byte(byte) do native_buffer_event.write_byte(byte)
+ redef fun write_bytes(bytes) do native_buffer_event.write(bytes.items, bytes.length)
+
# Write a file to the connection
#
- # require: `path.file_exists`
+ # If `not path.file_exists`, the method returns.
fun write_file(path: String)
do
- assert path.file_exists
-
var file = new FileReader.open(path)
- var output = native_buffer_event.output_buffer
- var fd = file.fd
- var length = file.file_stat.size
+ if file.last_error != null then
+ var error = new IOError("Failed to open file at '{path}'")
+ error.cause = file.last_error
+ self.last_error = error
+ file.close
+ return
+ end
+
+ var stat = file.file_stat
+ if stat == null then
+ last_error = new IOError("Failed to stat file at '{path}'")
+ file.close
+ return
+ end
- output.add_file(fd, 0, length)
+ var err = native_buffer_event.output_buffer.add_file(file.fd, 0, stat.size)
+ if err then
+ last_error = new IOError("Failed to add file at '{path}'")
+ file.close
+ end
end
end
# The input buffer associated to `self`
fun input_buffer: InputNativeEvBuffer `{ return bufferevent_get_input(self); `}
+
+ # Read data from this buffer
+ fun read_buffer(buf: NativeEvBuffer): Int `{ return bufferevent_read_buffer(self, buf); `}
+
+ # Write data to this buffer
+ fun write_buffer(buf: NativeEvBuffer): Int `{ return bufferevent_write_buffer(self, buf); `}
end
# A single buffer
# Examples
-Want to see `nitcorn` in action? Examples are available at ../../examples/nitcorn/src/.
+Want to see `nitcorn` in action? Examples are available at `examples/nitcorn/src/`.
# Features and TODO list
Stephan Michaud and Maxime Bélanger.
It has been adapted to a library, and is currently maintained, by Alexis Laferrière.
-
-Other contributors:
-
-* Alexandre Terrasa
all:
mkdir -p bin/
- ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
+ ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
xymus.net:
mkdir -p bin/
# See the License for the specific language governing permissions and
# limitations under the License.
-# Basic file server on port 80, usually requires `root` to execute
+# Basic file server on port 80 by default, may require `root` to execute
#
# To be safe, it is recommended to run this program with its own username:
# `sudo file_server -u nitcorn:www`
-module file_server_on_port_80
+module simple_file_server
import nitcorn
import privileges
# Prepare options
var opts = new OptionContext
var opt_drop = new OptionUserAndGroup.for_dropping_privileges
-opt_drop.mandatory = true
+var opt_port = new OptionInt("Server port", 80, "--port", "-p")
var opt_help = new OptionBool("Print this message", "--help", "-h")
-opts.add_option(opt_drop, opt_help)
-opts.parse(args)
+opts.add_option(opt_drop, opt_port, opt_help)
+opts.parse args
# Check options errors and help
if not opts.errors.is_empty or opt_help.value then
end
# Serve everything with a standard FilesHandler
-var vh = new VirtualHost("localhost:80")
+var vh = new VirtualHost("localhost:{opt_port.value}")
vh.routes.add new Route(null, new FileServer("www/hello_world/"))
var factory = new HttpFactory.and_libevent
factory.config.virtual_hosts.add vh
response.header["Content-Type"] = media_types["html"].as(not null)
else
# It's a single file
- var file = new FileReader.open(local_file)
- response.body = file.read_all
+ response.files.add local_file
var ext = local_file.file_extension
if ext != null then
response.header["Content-Type"] = media_type
else response.header["Content-Type"] = "application/octet-stream"
end
-
- file.close
end
else response = new HttpResponse(404)
# Body of this response
var body = "" is writable
+ # Files appended after `body`
+ var files = new Array[String]
+
# Finalize this response before sending it over HTTP
fun finalize
do
# Set the content length if not already set
if not header.keys.has("Content-Length") then
- header["Content-Length"] = body.bytelen.to_s
+ # Size of the body
+ var len = body.bytelen
+
+ # Size of included files
+ for path in files do
+ # TODO handle these error cases elsewhere, an error here will result in an invalid response
+ if not path.file_exists then
+ print_error "File does not exists at '{path}'"
+ continue
+ end
+
+ var stat = path.file_stat
+ if stat == null then
+ print_error "Failed to stat file at '{path}'"
+ continue
+ end
+
+ len += stat.size
+ end
+
+ # Set header
+ header["Content-Length"] = len.to_s
end
# Set server ID
tags=network,lib
maintainer=Alexis Laferrière <alexis.laf@xymus.net>
license=Apache-2.0
+more_contributors=Jean-Philippe Caissy <jean-philippe.caissy@shopify.com>, Guillaume Auger, Frederic Sevillano, Justin Michaud-Ouellette, Stephan Michaud, Maxime Bélanger
[upstream]
browse=https://github.com/nitlang/nit/tree/master/lib/nitcorn/
git=https://github.com/nitlang/nit.git
# Send back a response
write response.to_s
+ for path in response.files do write_file path
+
close
end
end
v.add_decl("int main(int argc, char** argv) \{")
end
+ v.add "#ifndef ANDROID"
v.add("signal(SIGABRT, sig_handler);")
v.add("signal(SIGFPE, sig_handler);")
v.add("signal(SIGILL, sig_handler);")
v.add("signal(SIGINT, sig_handler);")
v.add("signal(SIGTERM, sig_handler);")
v.add("signal(SIGSEGV, sig_handler);")
+ v.add "#endif"
v.add("signal(SIGPIPE, SIG_IGN);")
v.add("glob_argc = argc; glob_argv = argv;")
# Here we load an process all modules passed on the command line
var mmodules = modelbuilder.parse(arguments)
-if mmodules.is_empty then
- toolcontext.check_errors
- toolcontext.errors_info
- if toolcontext.error_count > 0 then exit(1) else exit(0)
-end
+if mmodules.is_empty then toolcontext.quit
modelbuilder.run_phases
v.add_abort("type null")
v.add("\}")
v.add("if({t}->table_size < 0) \{")
- v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
+ v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
v.add_abort("type dead")
v.add("\}")
end
if toolcontext.opt_only_parse.value then
self.toolcontext.info("*** ONLY PARSE...", 1)
- exit(0)
+ self.toolcontext.quit
end
return mmodules.to_a
if toolcontext.opt_only_parse.value then
self.toolcontext.info("*** ONLY PARSE...", 1)
- exit(0)
+ self.toolcontext.quit
end
return mmodules.to_a
if toolcontext.opt_only_metamodel.value then
self.toolcontext.info("*** ONLY METAMODEL", 1)
- exit(0)
+ toolcontext.quit
end
end
# REQUIRE: classes of imported modules are already build. (let `phase` do the job)
private fun build_classes(nmodule: AModule)
do
- var errcount = toolcontext.error_count
# Force building recursively
if nmodule.build_classes_is_done then return
nmodule.build_classes_is_done = true
if nimp != null then build_classes(nimp)
end
- if errcount != toolcontext.error_count then return
-
# Create all classes
# process AStdClassdef before so that non-AStdClassdef classes can be attached to existing ones, if any
for nclassdef in nmodule.n_classdefs do
self.build_a_mclass(nmodule, nclassdef)
end
- if errcount != toolcontext.error_count then return
-
# Create all classdefs
for nclassdef in nmodule.n_classdefs do
if not nclassdef isa AStdClassdef then continue
self.build_a_mclassdef(nmodule, nclassdef)
end
- if errcount != toolcontext.error_count then return
-
# Create inheritance on all classdefs
for nclassdef in nmodule.n_classdefs do
self.collect_a_mclassdef_inheritance(nmodule, nclassdef)
end
- if errcount != toolcontext.error_count then return
-
# Create the mclassdef hierarchy
for mclassdef in mmodule.mclassdefs do
mclassdef.add_in_hierarchy
end
- if errcount != toolcontext.error_count then return
-
# Check inheritance
for nclassdef in nmodule.n_classdefs do
self.check_supertypes(nmodule, nclassdef)
end
- if errcount != toolcontext.error_count then return
-
# Check unchecked ntypes
for nclassdef in nmodule.n_classdefs do
if nclassdef isa AStdClassdef then
end
end
- if errcount != toolcontext.error_count then return
-
# Check clash of ancestors
for nclassdef in nmodule.n_classdefs do
var mclassdef = nclassdef.mclassdef
end
end
- if errcount != toolcontext.error_count then return
-
# TODO: Check that the super-class is not intrusive
# Check that the superclasses are not already known (by transitivity)
for nclassdef in nmodule.n_classdefs do
- if not nclassdef isa AStdClassdef then continue
+ if not nclassdef isa AStdClassdef or nclassdef.is_broken then continue
var mclassdef = nclassdef.mclassdef
if mclassdef == null then continue
modelbuilder.run_phases
-if toolcontext.opt_only_metamodel.value then exit(0)
+if toolcontext.opt_only_metamodel.value then toolcontext.quit
var mainmodule = toolcontext.make_main_module(mmodules)
end
var contributors = mpackage.contributors
+ var more_contributors = mpackage.metadata("package.more_contributors")
+ if more_contributors != null then
+ for c in more_contributors.split(",") do
+ contributors.add c.trim
+ end
+ end
if not contributors.is_empty then
res.add "<h3>Contributors</h3>\n<ul class=\"box\">"
for c in contributors do
mmodules.add_all modelbuilder.parse(opt_mixins.value)
modelbuilder.run_phases
-if toolcontext.opt_only_metamodel.value then exit(0)
+if toolcontext.opt_only_metamodel.value then toolcontext.quit
var mainmodule: nullable MModule
redef fun name do return "android"
- redef fun supports_libgc do return true
+ redef fun supports_libgc do return false
redef fun supports_libunwind do return false
## Generate Application.mk
dir = "{android_project_root}/jni/"
"""
-APP_ABI := armeabi armeabi-v7a x86 mips
+APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-{{{app_target_api}}}
""".write_to_file "{dir}/Application.mk"
#node.debug("Unsafe typing: expected {sup}, got {sub}")
return sup
end
+ if sup isa MBottomType then return null # Skip error
if sub.need_anchor then
var u = anchor_to(sub)
self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
var mtype = v.visit_expr(g.n_expr)
if mtype == null then return
g.do_type_iterator(v, mtype)
+ if g.is_broken then is_broken = true
end
v.visit_stmt(n_block)
return tags.has("all") or tags.has(tag)
end
+ # Output all current stacked messages, total and exit the program
+ #
+ # If there is no error, exit with 0, else exit with 1.
+ fun quit
+ do
+ check_errors
+ errors_info
+ if error_count > 0 then exit(1) else exit(0)
+ end
+
# Output all current stacked messages
#
# Return true if no errors occurred.
# limitations under the License.
var i = [4, '4']
+i.first.output
var toto: Int
var toto: Object
end
+
+var a = new A
+a.toto.output
_toto = 't'
end
end
+
+var a = new A(1)
+a.m
end
class C[E: Object, F: Object]
end
+
+var c = new C
+c.output
class A[E: Object]
end
+
+var a = new A
+a.output
end
class A[E: Object]
end
+
+var a = new A
+a.output
# limitations under the License.
var i: Int = '4'
+i.output
# limitations under the License.
for i in 5 do
+ i.output
end
class A[T]
var k: T[Int]
end
+
+var a = new A[Object]
+a.output
class G[Foo]
type Bar: Object
end
+
+var g = new G[Object]
fun toto: Int
do
end
+
+toto.output
return 0
end
end
+
+toto.output
return 1
end
end
+
+toto.output
return 1
end
end
+
+toto.output
return 1
end
end
+
+toto.output
super G1
super G2
end
+
+var g = new G3
+g.output
# limitations under the License.
if 5 then
+ 1.output
end
super Array[Int]
super Array[Char]
end
+
+var a = new A
+a.output
super A
super B
end
+
+var c = new C
+c.output
super A[Int]
super B[Char]
end
+
+var c = new C
+c.output
super Array[E]
super List[F]
end
+
+var a = new A[Int, Int]
+a.output
class C
super A
end
+
+var a = new A
+a.output
redef class Object
var toto: Bool
end
+
+toto.output
redef class Int
var toto: Object
end
+
+1.toto.output
# limitations under the License.
if 5 and true then
+ 1.output
end
# limitations under the License.
while 5 do
+ 1.output
end
fun toto(a: Int) do end
fun toto(a: Char) do end
end
+
+var a = new A
+a.toto(1)
fun toto(a: Int) do end
fun toto(a: Int): Int do return 0 end
end
+
+var a = new A
+a.toto(1).output
class A end
class A end
+var a = new A
+a.output
redef class A end
redef class A end
+var a = new A
+a.output
import kernel
redef class Fail end
+
+var f = new Fail
+f.output
redef class C
redef var a: Int
end
+
+var c = new C(new B)
+c.a.output
redef class C
redef fun s do end
end
+
+var c = new C(new B)
+c.s.output
redef class C
redef fun r(x: C) do end
end
+
+var c = new C(new B)
+c.r
redef class C
redef fun r(x: B): B do return self end
end
+
+var c = new C(new B)
+c.r
redef class C
redef fun s: Int do return 1 end
end
+
+var c = new C(new B)
+c.s.output
do
return
end
+
+toto.output
do
return '4'
end
+
+(toto+1).output
# limitations under the License.
if true or 6 then
+ 1.output
end
super A
redef fun foo: Int do return 3
end
+
+var a = new A
+a.foo.output
+
+var b = new B
+b.foo
+
+var c = new C
+c.foo
super A
redef var a: Object = 2
end
+
+var b = new B
+b.a.output
super A
redef fun toto do end
end
+
+var b = new B
+b.toto.output
super A
redef fun toto(c: Object) do end
end
+
+var b = new B
+b.toto(true)
super A
redef fun toto(c: Char) do end
end
+
+var b = new B
+b.toto(true)
class B
super A
-redef fun toto: Int do return 2end
+redef fun toto: Int do return 2 end
end
+
+var b = new B
+b.toto.output
super A
redef fun toto: Char do return 'a' end
end
+
+var b = new B
+b.toto.output
super
end
end
+
+var a = new A
+a.foo
super Fail
end
+var a = new A
+a.output
super Array[Fail]
end
+var b = new B
+b.output
class D[T: Array[Fail]]
end
+
+var c = new C[Int]
+c.output
+var d = new D[Int]
+d.output
fun md: Array[Fail] do return 0
end
+var e = new E
+e.output
+var f = new F
+f.output
+var g = new G
+g.a.output
+g.ma(1)
+g.mb(1)
+g.mc(1).output
+g.md(1).output
# limitations under the License.
var i = new Canard
+i.output
+++ /dev/null
-Mandatory option -u, --usergroup not found.
-Usage: file_server [Options]
- -u, --usergroup Drop privileges to user:group or simply user
- --help, -h Print this message
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
Edge
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
----
=to=Entity#0:
=labels=Array(4):
8:MPropDef
13:MAttributeDef
=properties=JsonObject(5):
-{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["“Answer to the Ultimate Question of Life, the Universe, and Everything.","“"],"is_intro":true}
+{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["â\80\9cAnswer to the Ultimate Question of Life, the Universe, and Everything.","â\80\9c"],"is_intro":true}
Edge
--- /dev/null
+libevent warning: Opening localhost:80 failed
--- /dev/null
+{
+ "beer": "test",
+ "name": "Gaëa"
+}
+
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+
+var str = """{
+ "beer": "test",
+ "name": "Gaëa"
+}"""
+
+var json = str.parse_json
+
+assert json != null
+
+print json.to_pretty_json