Merge: Neo4j: bug fixes and improvments
authorJean Privat <jean@pryen.org>
Mon, 21 Jul 2014 14:44:32 +0000 (10:44 -0400)
committerJean Privat <jean@pryen.org>
Mon, 21 Jul 2014 14:44:32 +0000 (10:44 -0400)
Changes:
* avoid node duplication by force loading nodes from local store
* better handling of big graphes through separation of nodes and edges loading
* try to speed things up with nodes data preloading from `nodes_with_labels` response

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

28 files changed:
lib/sqlite3/native_sqlite3.nit [moved from lib/sqlite3.nit with 79% similarity]
lib/sqlite3/sqlite3.nit [new file with mode: 0644]
src/abstract_compiler.nit
src/android_annotations.nit
src/android_platform.nit
src/annotation.nit [new file with mode: 0644]
src/cached.nit
src/common_ffi/c_compiler_options.nit
src/common_ffi/extra_java_files.nit
src/common_ffi/pkgconfig.nit
src/compiler_ffi.nit
src/global_compiler.nit
src/model/README.md [new file with mode: 0644]
src/model/model.nit
src/parser/README [deleted file]
src/parser/README.md [new file with mode: 0644]
src/platform.nit
src/separate_compiler.nit
src/separate_erasure_compiler.nit
src/vm.nit
tests/sav/fixme/nitvm_args3.res [new file with mode: 0644]
tests/sav/test_sqlite3_native.res [moved from tests/sav/test_sqlite3.res with 100% similarity]
tests/sav/test_sqlite3_nity.res [new file with mode: 0644]
tests/sav/test_sqlite3_nity_alt1.res [new file with mode: 0644]
tests/sav/test_sqlite3_nity_alt2.res [new file with mode: 0644]
tests/test_ffi_c_lots_of_refs.nit [new file with mode: 0644]
tests/test_sqlite3_native.nit [moved from tests/test_sqlite3.nit with 89% similarity]
tests/test_sqlite3_nity.nit [new file with mode: 0644]

similarity index 79%
rename from lib/sqlite3.nit
rename to lib/sqlite3/native_sqlite3.nit
index 6fe0828..da93820 100644 (file)
 # limitations under the License.
 
 # Low-level Sqlite3 features
-module sqlite3 is pkgconfig("sqlite3")
+module native_sqlite3 is pkgconfig("sqlite3")
 
 in "C header" `{
        #include <sqlite3.h>
 `}
 
+redef class Sys
+       # Last error raised when calling `Sqlite3::open`
+       var sqlite_open_error: nullable Sqlite3Code = null
+end
+
 extern class Sqlite3Code `{int`}
        new ok `{ return SQLITE_OK; `} #         0   /* Successful result */
        fun is_ok: Bool `{ return recv == SQLITE_OK; `}
@@ -73,7 +78,7 @@ extern class Sqlite3Code `{int`}
 end
 
 # A prepared statement
-extern class Statement `{sqlite3_stmt*`}
+extern class NativeStatement `{sqlite3_stmt*`}
 
        # Evaluate the statement
        fun step: Sqlite3Code `{
@@ -89,6 +94,7 @@ extern class Statement `{sqlite3_stmt*`}
                return NativeString_to_s(ret);
        `}
 
+       # Number of bytes in the blob or string at row `i`
        fun column_bytes(i: Int) : Int `{
                return sqlite3_column_bytes(recv, i);
        `}
@@ -101,15 +107,12 @@ extern class Statement `{sqlite3_stmt*`}
                return sqlite3_column_int(recv, i);
        `}
 
-       fun column_text(i: Int) : String import NativeString.to_s `{
-               char * ret = (char *) sqlite3_column_text(recv, i);
-               if( ret == NULL ){
-                       ret = "";
-               }
-               return NativeString_to_s(ret);
+       fun column_text(i: Int): NativeString `{
+               return (char *)sqlite3_column_text(recv, i);
        `}
 
-       fun column_type(i: Int) : Int `{
+       # Type of the entry at row `i`
+       fun column_type(i: Int): DataType `{
                return sqlite3_column_type(recv, i);
        `}
 
@@ -127,15 +130,21 @@ extern class Statement `{sqlite3_stmt*`}
 end
 
 # A database connection
-extern class Sqlite3 `{sqlite3 *`}
+extern class NativeSqlite3 `{sqlite3 *`}
 
        # Open a connection to a database in UTF-8
-       new open(filename: String) import String.to_cstring `{
+       new open(filename: String) import String.to_cstring, set_sys_sqlite_open_error `{
                sqlite3 *self = NULL;
-               sqlite3_open(String_to_cstring(filename), &self);
+               int err = sqlite3_open(String_to_cstring(filename), &self);
+               NativeSqlite3_set_sys_sqlite_open_error(self, (void*)(long)err);
+               // The previous cast is a hack, using non pointers in extern classes is not
+               // yet in the spec of the FFI.
                return self;
        `}
 
+       # Utility method to set `Sys.sqlite_open_error`
+       private fun set_sys_sqlite_open_error(err: Sqlite3Code) do sys.sqlite_open_error = err
+
        # Has this DB been correctly opened?
        #
        # To know if it has been closed or interrupted, you must check for errors with `error`.
@@ -152,13 +161,13 @@ extern class Sqlite3 `{sqlite3 *`}
        `}
 
        # Prepare a SQL statement
-       fun prepare(sql: String): nullable Statement import String.to_cstring, Statement.as nullable `{
+       fun prepare(sql: String): nullable NativeStatement import String.to_cstring, NativeStatement.as nullable `{
                sqlite3_stmt *stmt;
                int res = sqlite3_prepare_v2(recv, String_to_cstring(sql), -1, &stmt, 0);
                if (res == SQLITE_OK)
-                       return Statement_as_nullable(stmt);
+                       return NativeStatement_as_nullable(stmt);
                else
-                       return null_Statement();
+                       return null_NativeStatement();
        `}
 
        fun last_insert_rowid: Int `{
@@ -169,3 +178,14 @@ extern class Sqlite3 `{sqlite3 *`}
                return sqlite3_errcode(recv);
        `}
 end
+
+# Sqlite data types
+extern class DataType `{ int `}
+       fun is_integer: Bool `{ return recv == SQLITE_INTEGER; `}
+       fun is_float: Bool `{ return recv == SQLITE_FLOAT; `}
+       fun is_blob: Bool `{ return recv == SQLITE_BLOB; `}
+       fun is_null: Bool `{ return recv == SQLITE_NULL; `}
+       fun is_text: Bool `{ return recv == SQLITE_TEXT; `}
+
+       fun to_i: Int `{ return recv; `}
+end
diff --git a/lib/sqlite3/sqlite3.nit b/lib/sqlite3/sqlite3.nit
new file mode 100644 (file)
index 0000000..d194362
--- /dev/null
@@ -0,0 +1,307 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 201 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Services to manipulate a Sqlite3 database
+#
+# For more information, refer to the documentation of http://www.sqlite.org/docs.html
+module sqlite3
+
+private import native_sqlite3
+import standard
+
+# A connection to a Sqlite3 database
+class Sqlite3DB
+       private var native_connection: NativeSqlite3
+
+       # Is this connection to the DB open?
+       var is_open = false
+
+       # All `Statement` opened from this connection that must be closed with this connection
+       private var open_statements = new Array[Statement]
+
+       # Open a connection to the database file at `path`
+       init open(path: Text)
+       do
+               native_connection = new NativeSqlite3.open(path.to_s)
+               if native_connection.is_valid then is_open = true
+       end
+
+       # Close this connection to the DB and all open statements
+       fun close
+       do
+               is_open = false
+
+               # close open statements
+               for stmt in open_statements do if stmt.is_open then
+                       stmt.close
+               end
+
+               native_connection.close
+       end
+
+       # Prepare and return a `Statement`, return `null` on error
+       fun prepare(sql: Text): nullable Statement
+       do
+               var native_stmt = native_connection.prepare(sql.to_s)
+               if native_stmt == null then return null
+
+               var stmt = new Statement(native_stmt)
+               open_statements.add stmt
+               return stmt
+       end
+
+       # Execute the `sql` statement and return `true` on success
+       fun execute(sql: Text): Bool
+       do
+               var err = native_connection.exec(sql.to_s)
+               return err.is_ok
+       end
+
+       # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun create_table(rest: Text): Bool do return execute("CREATE TABLE " + rest)
+
+       # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun insert(rest: Text): Bool do return execute("INSERT " + rest)
+
+       # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun replace(rest: Text): Bool do return execute("REPLACE " + rest)
+
+       # Select from the DB with a statement beginning with "SELECT ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun select(rest: Text): nullable Statement do return prepare("SELECT " + rest)
+
+       # TODO add more prefix here as needed
+
+       # The latest error message, or `null` if there is none
+       fun error: nullable String
+       do
+               if not native_connection.is_valid then
+                       var err = sys.sqlite_open_error
+                       if err == null then return null
+                       return err.to_s
+               end
+
+               var err = native_connection.error
+               if err.is_ok then return null
+               return err.to_s
+       end
+end
+
+# A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
+class Statement
+       private var native_statement: NativeStatement
+
+       private init(ns: NativeStatement) do self.native_statement = ns
+
+       # Is this statement usable?
+       var is_open = true
+
+       # Close and finalize this statement
+       fun close
+       do
+               is_open = false
+               native_statement.finalize
+       end
+
+       # Reset this statement and return a `StatementIterator` to iterate over the result
+       fun iterator: StatementIterator
+       do
+               native_statement.reset
+               native_statement.step
+               return new StatementIterator(self)
+       end
+end
+
+class StatementRow
+       # Statement linked to `self`
+       var statement: Statement
+
+       private init(s: Statement) do self.statement = s
+
+       # Number of entries in this row
+       #
+       # require: `self.statement.is_open`
+       fun length: Int
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_count
+       end
+
+       # Returns the `i`th entry on this row
+       fun [](i: Int): StatementEntry do return new StatementEntry(statement, i)
+end
+
+# An entry on a `StatementRow`
+class StatementEntry
+       # Statement linked to `self`
+       var statement: Statement
+
+       private var index: Int
+
+       private init(s: Statement, i: Int)
+       do
+               self.statement = s
+               self.index = i
+       end
+
+       # Name of the column
+       #
+       # require: `self.statement.is_open`
+       fun name: String is cached do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_name(index)
+       end
+
+       # Get the value of this entry according to its Sqlite type
+       #
+       # require: `self.statement.is_open`
+       fun value: nullable Sqlite3Data
+       do
+               assert statement_closed: statement.is_open
+
+               var data_type = statement.native_statement.column_type(index)
+               if data_type.is_integer then return to_i
+               if data_type.is_float then return to_f
+               if data_type.is_blob then return to_blob
+               if data_type.is_null then return null
+               if data_type.is_text then return to_s
+               abort
+       end
+
+       # Get this entry as `Int`
+       #
+       # If the Sqlite type of this entry is not an integer, it will be `CAST` to
+       # integer. If `null`, returns 0.
+       #
+       # require: `self.statement.is_open`
+       fun to_i: Int
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_int(index)
+       end
+
+       # Get this entry as `Float`
+       #
+       # If the Sqlite type of this entry is not a floating point, it will be `CAST`
+       # to float. If `null`, returns 0.0.
+       #
+       # require: `self.statement.is_open`
+       fun to_f: Float
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_double(index)
+       end
+
+       # Get this entry as `String`
+       #
+       # If the Sqlite type of this entry is not text, it will be `CAST` to text.
+       # If null, returns an empty string.
+       #
+       # require: `self.statement.is_open`
+       redef fun to_s
+       do
+               assert statement_closed: statement.is_open
+
+               var native_string = statement.native_statement.column_text(index)
+               if native_string.address_is_null then return ""
+               return native_string.to_s
+       end
+
+       # Get this entry as `Blob`
+       #
+       # If the Sqlite type of this entry is not a blob, it will be `CAST` to text.
+       # If null, returns a NULL pointer.
+       #
+       # require: `self.statement.is_open`
+       fun to_blob: Blob
+       do
+               assert statement_closed: statement.is_open
+
+               # By spec, we must get the pointer before the byte count
+               var pointer = statement.native_statement.column_blob(index)
+               var length = statement.native_statement.column_bytes(index)
+
+               return new Blob(pointer, length)
+       end
+end
+
+# Iterator over the rows of a statement result
+class StatementIterator
+       super Iterator[StatementRow]
+
+       # Statement linked to `self`
+       var statement: Statement
+
+       private init(s: Statement)
+       do
+               self.statement = s
+               self.item = new StatementRow(s)
+       end
+
+       redef var item: StatementRow
+
+       redef var is_ok = true
+
+       # require: `self.statement.is_open`
+       redef fun next
+       do
+               assert statement_closed: statement.is_open
+
+               var err = statement.native_statement.step
+               if err.is_row then
+                       is_ok = true
+               else if err.is_done then
+                       # Clean complete
+                       is_ok = false
+               else
+                       # error
+                       # FIXME do something with the error?
+                       is_ok = false
+               end
+       end
+end
+
+# A data type supported by Sqlite3
+interface Sqlite3Data end
+
+redef universal Int super Sqlite3Data end
+redef universal Float super Sqlite3Data end
+redef class String super Sqlite3Data end
+
+# A Sqlite3 blob
+class Blob
+       super Sqlite3Data
+
+       private init(pointer: Pointer, length: Int)
+       do
+               self.pointer = pointer
+               self.length = length
+       end
+
+       var pointer: Pointer
+       var length: Int
+end
index 1296ddb..a2c08de 100644 (file)
@@ -578,7 +578,40 @@ abstract class AbstractCompiler
        protected fun compile_header_structs is abstract
 
        # Declaration of structures for nitni undelying the FFI
-       protected fun compile_nitni_structs is abstract
+       protected fun compile_nitni_structs
+       do
+               self.header.add_decl """
+/* Native reference to Nit objects */
+/* This structure is used to represent every Nit type in extern methods and custom C code. */
+struct nitni_ref {
+       struct nitni_ref *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+};
+
+/* List of global references from C code to Nit objects */
+/* Instanciated empty at init of Nit system and filled explicitly by user in C code */
+struct nitni_global_ref_list_t {
+       struct nitni_ref *head, *tail;
+};
+extern struct nitni_global_ref_list_t *nitni_global_ref_list;
+
+/* Initializer of global reference list */
+extern void nitni_global_ref_list_init();
+
+/* Intern function to add a global reference to the list */
+extern void nitni_global_ref_add( struct nitni_ref *ref );
+
+/* Intern function to remove a global reference from the list */
+extern void nitni_global_ref_remove( struct nitni_ref *ref );
+
+/* Increase count on an existing global reference */
+extern void nitni_global_ref_incr( struct nitni_ref *ref );
+
+/* Decrease count on an existing global reference */
+extern void nitni_global_ref_decr( struct nitni_ref *ref );
+"""
+       end
 
        # Generate the main C function.
        # This function:
@@ -688,6 +721,9 @@ abstract class AbstractCompiler
 
                v.add("glob_argc = argc; glob_argv = argv;")
                v.add("initialize_gc_option();")
+
+               v.add "initialize_nitni_global_refs();"
+
                var main_type = mainmodule.sys_type
                if main_type != null then
                        var mainmodule = v.compiler.mainmodule
@@ -747,6 +783,67 @@ abstract class AbstractCompiler
                v.add("\}")
        end
 
+       # Copile all C functions related to the [incr|decr]_ref features of the FFI
+       fun compile_nitni_global_ref_functions
+       do
+               var v = self.new_visitor
+               v.add """
+struct nitni_global_ref_list_t *nitni_global_ref_list;
+void initialize_nitni_global_refs() {
+       nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
+       nitni_global_ref_list->head = NULL;
+       nitni_global_ref_list->tail = NULL;
+}
+
+void nitni_global_ref_add( struct nitni_ref *ref ) {
+       if ( nitni_global_ref_list->head == NULL ) {
+               nitni_global_ref_list->head = ref;
+               ref->prev = NULL;
+       } else {
+               nitni_global_ref_list->tail->next = ref;
+               ref->prev = nitni_global_ref_list->tail;
+       }
+       nitni_global_ref_list->tail = ref;
+
+       ref->next = NULL;
+}
+
+void nitni_global_ref_remove( struct nitni_ref *ref ) {
+       if ( ref->prev == NULL ) {
+               nitni_global_ref_list->head = ref->next;
+       } else {
+               ref->prev->next = ref->next;
+       }
+
+       if ( ref->next == NULL ) {
+               nitni_global_ref_list->tail = ref->prev;
+       } else {
+               ref->next->prev = ref->prev;
+       }
+}
+
+extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
+       if ( ref->count == 0 ) /* not registered */
+       {
+               /* add to list */
+               nitni_global_ref_add( ref );
+       }
+
+       ref->count ++;
+}
+
+extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
+       if ( ref->count == 1 ) /* was last reference */
+       {
+               /* remove from list */
+               nitni_global_ref_remove( ref );
+       }
+
+       ref->count --;
+}
+"""
+       end
+
        # List of additional files required to compile (FFI)
        var extern_bodies = new Array[ExternFile]
 
index 39037e2..cad98bd 100644 (file)
@@ -23,6 +23,7 @@ import modelbuilder
 import modelize_property
 import literal
 import typing
+private import annotation
 
 # Metadata associated to an Android project
 class AndroidProject
@@ -45,13 +46,13 @@ class AndroidProject
        var manifest_application_lines = new Array[String]
 
        # Minimum API level required for the application to run
-       var min_sdk: nullable Int = null
+       var min_api: nullable Int = null
 
        # Build target API level
-       var target_sdk: nullable Int = null
+       var target_api: nullable Int = null
 
        # Maximum API level on which the application will be allowed to run
-       var max_sdk: nullable Int = null
+       var max_api: nullable Int = null
 
        redef fun to_s do return """
 name: {{{name or else "null"}}}
@@ -74,20 +75,20 @@ redef class ModelBuilder
                annot = priority_annotation_on_modules("java_package", mmodule)
                if annot != null then project.java_package = annot.arg_as_string(self)
 
-               var annots = collect_annotations_on_modules("min_sdk_version", mmodule)
-               for an in annots do project.min_sdk = an.arg_as_int(self)
+               var annots = collect_annotations_on_modules("min_api_version", mmodule)
+               for an in annots do project.min_api = an.arg_as_int(self)
 
-               annots = collect_annotations_on_modules("max_sdk_version", mmodule)
-               for an in annots do project.max_sdk = an.arg_as_int(self)
+               annots = collect_annotations_on_modules("max_api_version", mmodule)
+               for an in annots do project.max_api = an.arg_as_int(self)
 
-               annots = collect_annotations_on_modules("target_sdk_version", mmodule)
-               for an in annots do project.target_sdk = an.arg_as_int(self)
+               annots = collect_annotations_on_modules("target_api_version", mmodule)
+               for an in annots do project.target_api = an.arg_as_int(self)
 
                annots = collect_annotations_on_modules("android_manifest", mmodule)
-               for an in annots do project.manifest_lines.add an.arg_as_string(self)
+               for an in annots do project.manifest_lines.add an.arg_as_string(self) or else ""
 
                annots = collect_annotations_on_modules("android_manifest_application", mmodule)
-               for an in annots do project.manifest_application_lines.add an.arg_as_string(self)
+               for an in annots do project.manifest_application_lines.add an.arg_as_string(self) or else ""
 
                # Get the date and time (down to the minute) as string
                var local_time = new Tm.localtime
@@ -151,62 +152,6 @@ redef class ModelBuilder
 end
 
 redef class AAnnotation
-       # Get the single argument of `self` as a `String`. Raise error on any inconsistency.
-       private fun arg_as_string(modelbuilder: ModelBuilder): String
-       do
-               var annotation_name = n_atid.n_id.text
-               var format_error = "Annotation error: \"{annotation_name}\" expects a single String as argument."
-
-               var args = n_args
-               var platform_name
-               if args.length != 1 then
-                       modelbuilder.error(self, format_error)
-                       return ""
-               else
-                       var arg = args.first
-                       
-                       if not arg isa AExprAtArg then
-                               modelbuilder.error(self, format_error)
-                               return ""
-                       end
-
-                       var expr = arg.n_expr
-                       if not expr isa AStringFormExpr then
-                               modelbuilder.error(self, format_error)
-                               return ""
-                       end
-                       return expr.value.as(not null)
-               end
-       end
-
-       # Get the single argument of `self` as an `Int`. Raise error on any inconsistency.
-       private fun arg_as_int(modelbuilder: ModelBuilder): nullable Int
-       do
-               var annotation_name = n_atid.n_id.text
-               var format_error = "Annotation error: \"{annotation_name}\" expects a single Int as argument."
-
-               var args = n_args
-               var platform_name
-               if args.length != 1 then
-                       modelbuilder.error(self, format_error)
-                       return null
-               else
-                       var arg = args.first
-                       
-                       if not arg isa AExprAtArg then
-                               modelbuilder.error(self, format_error)
-                               return null
-                       end
-
-                       var expr = arg.n_expr
-                       if not expr isa AIntExpr then
-                               modelbuilder.error(self, format_error)
-                               return null
-                       end
-                       return expr.value.as(not null)
-               end
-       end
-
        # Returns a version string (example: "1.5.6b42a7c") from an annotation `version(1, 5, git_revision)`.
        #
        # The user can enter as many fields as needed. The call to `git_revision` will be replaced by the short
@@ -219,33 +164,26 @@ redef class AAnnotation
                var args = n_args
                var platform_name
                if args.length < 1 then
-                       modelbuilder.error(self, "Annotation error: \"{annotation_name}\" expects at least a single argument.")
+                       modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.")
                        return ""
                else
                        for arg in args do
-                               var format_error = "Annotation error: \"{annotation_name}\" expects its arguments to be of type Int or a call to `git_revision`"
+                               var format_error = "Annotation error: \"{name}\" expects its arguments to be of type Int or a call to `git_revision`"
                                
-                               if not arg isa AExprAtArg then
-                                       modelbuilder.error(self, format_error)
-                                       return ""
+                               var value
+                               value = arg.as_int
+                               if value != null then
+                                       version_fields.add value
+                                       continue
                                end
 
-                               var expr = arg.n_expr
-                               if expr isa AIntExpr then
-                                       var value = expr.value
-                                       assert value != null
+                               value = arg.as_string
+                               if value != null then
                                        version_fields.add value
-                               else if expr isa AStringFormExpr then
-                                       version_fields.add expr.value.as(not null)
-                               else if expr isa ACallExpr then
-                                       # We support calls to "git" only
-                                       var exec_args = expr.n_args.to_a
-                                       if expr.n_id.text != "git_revision" or not exec_args.is_empty then
-                                               modelbuilder.error(self,
-                                                       "Annotation error: \"{annotation_name}\" accepts only calls to `git_revision` with the command as arguments.")
-                                               return ""
-                                       end
+                               end
 
+                               value = arg.as_id
+                               if value == "git_revision" then
                                        # Get Git short revision
                                        var proc = new IProcess("git", "rev-parse", "--short", "HEAD")
                                        proc.wait
@@ -261,10 +199,11 @@ redef class AAnnotation
                                        if dirty then revision += ".d"
 
                                        version_fields.add revision
-                               else
-                                       modelbuilder.error(self, format_error)
-                                       return ""
+                                       continue
                                end
+
+                               modelbuilder.error(self, format_error)
+                               return ""
                        end
                end
 
index 79be326..7de42f4 100644 (file)
@@ -67,14 +67,14 @@ class AndroidToolchain
                var app_version = project.version
                if app_version == null then app_version = "1.0"
 
-               var app_min_sdk = project.min_sdk
-               if app_min_sdk == null then app_min_sdk = 10
+               var app_min_api = project.min_api
+               if app_min_api == null then app_min_api = 10
 
-               var app_target_sdk = project.target_sdk
-               if app_target_sdk == null then app_target_sdk = app_min_sdk
+               var app_target_api = project.target_api
+               if app_target_api == null then app_target_api = app_min_api
 
-               var app_max_sdk = ""
-               if project.max_sdk != null then app_max_sdk = "android:maxSdkVersion=\"{app_max_sdk}\""
+               var app_max_api = ""
+               if project.max_api != null then app_max_api = "android:maxSdkVersion=\"{project.max_api.as(not null)}\""
 
                # Clear the previous android project, so there is no "existing project warning"
                # or conflict between Java files of different projects
@@ -83,7 +83,7 @@ class AndroidToolchain
                var args = ["android", "-s",
                        "create", "project",
                        "--name", short_project_name,
-                       "--target", "android-{app_target_sdk}",
+                       "--target", "android-{app_target_api}",
                        "--path", android_project_root,
                        "--package", app_package,
                        "--activity", short_project_name]
@@ -139,9 +139,9 @@ $(call import-module,android/native_app_glue)
 
     <!-- This is the platform API where NativeActivity was introduced. -->
     <uses-sdk 
-        android:minSdkVersion="{{{app_min_sdk}}}" 
-        android:targetSdkVersion="{{{app_target_sdk}}}" 
-        {{{app_max_sdk}}} /> 
+        android:minSdkVersion="{{{app_min_api}}}" 
+        android:targetSdkVersion="{{{app_target_api}}}" 
+        {{{app_max_api}}} /> 
 
     <application
                android:label="@string/app_name"
diff --git a/src/annotation.nit b/src/annotation.nit
new file mode 100644 (file)
index 0000000..4daafef
--- /dev/null
@@ -0,0 +1,90 @@
+# 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.
+
+# Management and utilities on annotations
+module annotation
+
+import parser
+import modelbuilder
+import literal
+
+redef class AAnnotation
+       # The name of the annotation
+       fun name: String
+       do
+               return n_atid.n_id.text
+       end
+
+       # Get the single argument of `self` as a `String`.
+       # Raise error and return null on any inconsistency.
+       fun arg_as_string(modelbuilder: ModelBuilder): nullable String
+       do
+               var args = n_args
+               if args.length == 1 then
+                       var arg = args.first.as_string
+                       if arg != null then return arg
+               end
+
+               modelbuilder.error(self, "Annotation error: \"{name}\" expects a single String as argument.")
+               return null
+       end
+
+       # Get the single argument of `self` as an `Int`.
+       # Raise error and return null on any inconsistency.
+       fun arg_as_int(modelbuilder: ModelBuilder): nullable Int
+       do
+               var args = n_args
+               if args.length == 1 then
+                       var arg = args.first.as_int
+                       if arg != null then return arg
+               end
+
+               modelbuilder.error(self, "Annotation error: \"{name}\" expects a single Int as argument.")
+               return null
+       end
+end
+
+redef class AAtArg
+       # Get `self` as a `String`.
+       # Return null if not a string.
+       fun as_string: nullable String
+       do
+               if not self isa AExprAtArg then return null
+               var nexpr = n_expr
+               if not nexpr isa AStringFormExpr then return null
+               return nexpr.value.as(not null)
+       end
+
+       # Get `self` as an `Int`.
+       # Return null if not an integer.
+       fun as_int: nullable Int
+       do
+               if not self isa AExprAtArg then return null
+               var nexpr = n_expr
+               if not nexpr isa AIntExpr then return null
+               return nexpr.value.as(not null)
+       end
+
+       # Get `self` as a single identifier.
+       # Return null if not a single identifier.
+       fun as_id: nullable String
+       do
+               if not self isa AExprAtArg then return null
+               var nexpr = n_expr
+               if not nexpr isa ACallExpr then return null
+               if not nexpr.n_expr isa AImplicitSelfExpr then return null
+               if not nexpr.n_args.n_exprs.is_empty then return null
+               return nexpr.n_id.text
+       end
+end
index 1ca9256..ead285f 100644 (file)
@@ -21,6 +21,7 @@ module cached
 import modelize_property
 import parser_util
 import simple_misc_analysis
+private import annotation
 
 redef class ToolContext
        var cached_phase: Phase = new CachedPhase(self, [modelize_property_phase])
@@ -41,7 +42,7 @@ private class CachedPhase
        redef fun process_annotated_node(npropdef, nat)
        do
                # Skip if we are not interested
-               if nat.n_atid.n_id.text != "cached" then return
+               if nat.name != "cached" then return
 
                # Do some validity checks and print errors if the annotation is used incorrectly
                var modelbuilder = toolcontext.modelbuilder
index ca64759..7717973 100644 (file)
@@ -21,6 +21,7 @@ module c_compiler_options
 
 import c
 import cpp
+private import annotation
 
 redef class ToolContext
        var c_compiler_options_phase: Phase = new CCompilerOptionsPhase(self, null)
@@ -36,7 +37,7 @@ private class CCompilerOptionsPhase
        redef fun process_annotated_node(nmoduledecl, nat)
        do
                # Skip if we are not interested
-               var annotation_name = nat.n_atid.n_id.text
+               var annotation_name = nat.name
                if annotation_name != compiler_annotation_name and
                   annotation_name != linker_annotation_name and
                   annotation_name != cpp_compiler_annotation_name then return
index 632a322..67a5f9d 100644 (file)
@@ -21,6 +21,7 @@ module extra_java_files
 
 import literal
 import java
+private import annotation
 
 redef class ToolContext
        var extra_java_files_phase: Phase = new JavaExtraFilesPhase(self, [literal_phase])
@@ -38,7 +39,7 @@ private class JavaExtraFilesPhase
        do
                # Skip if we are not interested
                var annot_name = "extra_java_files"
-               if nat.n_atid.n_id.text != annot_name then return
+               if nat.name != annot_name then return
 
                # Do some validity checks and print errors if the annotation is used incorrectly
                var modelbuilder = toolcontext.modelbuilder
@@ -65,20 +66,12 @@ private class JavaExtraFilesPhase
 
                var format_error = "Syntax error: \"{annot_name}\" expects its arguments to be paths to java files."
                for arg in args do
-                       if not arg isa AExprAtArg then
-                               modelbuilder.error(nat, format_error)
+                       var path = arg.as_string
+                       if path == null then
+                               modelbuilder.error(arg, format_error)
                                return
                        end
 
-                       var expr = arg.n_expr
-                       if not expr isa AStringFormExpr then
-                               modelbuilder.error(nat, format_error)
-                               return
-                       end
-
-                       var path = expr.value
-                       assert path != null
-
                        # Append specified path to directory of the Nit source file
                        var source_file = nat.location.file
                        if source_file != null then path = "{source_file.filename.dirname}/{path}"
index b263834..5306b97 100644 (file)
 module pkgconfig
 
 import c
+private import annotation
 
 redef class ToolContext
-       var pkgconfig_phase: Phase = new PkgconfigPhase(self, null)
+       var pkgconfig_phase: Phase = new PkgconfigPhase(self, [literal_phase])
 end
 
 # Detects the `pkgconfig` annotation on the module declaration only.
@@ -31,7 +32,7 @@ class PkgconfigPhase
        redef fun process_annotated_node(nmoduledecl, nat)
        do
                # Skip if we are not interested
-               if nat.n_atid.n_id.text != "pkgconfig" then return
+               if nat.name != "pkgconfig" then return
 
                # Do some validity checks and print errors if the annotation is used incorrectly
                var modelbuilder = toolcontext.modelbuilder
@@ -49,19 +50,12 @@ class PkgconfigPhase
 
                var pkgs = new Array[String]
                for arg in args do
-                       if not arg isa AExprAtArg then
+                       var pkg = arg.as_string
+                       if pkg == null then
                                modelbuilder.error(nat, "Syntax error: \"pkgconfig\" expects its arguments to be the name of the package as String literals.")
                                return
                        end
 
-                       var expr = arg.n_expr
-                       if not expr isa AStringFormExpr then
-                               modelbuilder.error(nat, "Syntax error: \"pkgconfig\" expects its arguments to be the name of the package as String literals.")
-                               return
-                       end
-
-                       var pkg = expr.collect_text
-                       pkg = pkg.substring(1, pkg.length-2)
                        pkgs.add(pkg)
                end
 
index 14ae43f..9a83ba4 100644 (file)
@@ -45,6 +45,10 @@ redef class MModule
                ensure_compile_nitni_base(v)
 
                nitni_ccu.header_c_types.add("#include \"{name}._ffi.h\"\n")
+               nitni_ccu.header_c_types.add """
+extern void nitni_global_ref_incr(void*);
+extern void nitni_global_ref_decr(void*);
+"""
 
                nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
 
@@ -178,7 +182,7 @@ redef class AMethPropdef
                                arguments_for_c.add(arg.name)
                        else
                                v.add("struct nitni_instance* var_for_c_{a};")
-                               v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
+                               v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
                                v.add("var_for_c_{a}->value = {arg.name};")
                                arguments_for_c.add("var_for_c_{a}")
                        end
@@ -235,7 +239,7 @@ redef class AMethPropdef
                                arguments_for_c.add(arg.name)
                        else
                                v.add("struct nitni_instance* var_for_c_{a};")
-                               v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
+                               v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
                                v.add("var_for_c_{a}->value = {arg.name};")
                                arguments_for_c.add("var_for_c_{a}")
                        end
@@ -294,7 +298,7 @@ redef class AbstractCompilerVisitor
                        add("return {src};")
                else
                        add("struct nitni_instance* ret_for_c;")
-                       add("ret_for_c = malloc(sizeof(struct nitni_instance));")
+                       add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
                        add("ret_for_c->value = {src};")
                        add("return ret_for_c;")
                end
@@ -316,14 +320,17 @@ redef class MType
        private fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
        do
                # actually, we do not need to do anything when using the bohem garbage collector
+               var call_context = from_c_call_context
 
                # incr_ref
-               var nitni_visitor = v.compiler.new_visitor
-               ccu.header_decl.add("#define {mangled_cname}_incr_ref(from) while(0)\{\}\n")
-
-               # incr_ref
-               nitni_visitor = v.compiler.new_visitor
-               ccu.header_decl.add("#define {mangled_cname}_decr_ref(from) while(0)\{\}\n")
+               ccu.header_decl.add "#ifndef {mangled_cname}_incr_ref\n"
+               ccu.header_decl.add "   #define {mangled_cname}_incr_ref(from) nitni_global_ref_incr(({call_context.name_mtype(self)})(from))\n"
+               ccu.header_decl.add "#endif\n"
+
+               # decr_ref
+               ccu.header_decl.add "#ifndef {mangled_cname}_decr_ref\n"
+               ccu.header_decl.add "   #define {mangled_cname}_decr_ref(from) nitni_global_ref_decr(({call_context.name_mtype(self)})(from))\n"
+               ccu.header_decl.add "#endif\n"
        end
 end
 
@@ -354,7 +361,7 @@ redef class MNullableType
                var full_internal_csignature = "{cname_blind} {full_cname}()"
                nitni_visitor.add("{full_internal_csignature} \{")
                nitni_visitor.add("struct nitni_instance* ret_for_c;")
-               nitni_visitor.add("ret_for_c = malloc(sizeof(struct nitni_instance));")
+               nitni_visitor.add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
                nitni_visitor.add("ret_for_c->value = NULL;")
                nitni_visitor.add("return ret_for_c;")
                nitni_visitor.add("\}")
index 9e79281..322fcda 100644 (file)
@@ -77,6 +77,7 @@ redef class ModelBuilder
                end
 
                # The main function of the C
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # Compile until all runtime_functions are visited
@@ -269,7 +270,14 @@ class GlobalCompiler
 
        redef fun compile_nitni_structs
        do
-               self.header.add_decl("struct nitni_instance \{ val *value; \};")
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       val *value;
+\};"""
+               super
        end
 end
 
diff --git a/src/model/README.md b/src/model/README.md
new file mode 100644 (file)
index 0000000..5e5d735
--- /dev/null
@@ -0,0 +1,43 @@
+The meta model of Nit programs
+
+These modules define the entities of the Nit meta-model like modules, classes, types and properties
+They also provide an API to build and query models.
+All model classes starts with the `M` letter (`MModule`, `MClass`, etc.)
+
+The model is used by tools that need a higher view than a AST (see `parser`).
+The model represents What the programmer means.
+
+Because of the specification of the Nit language, the model is complex and sometime difficult to understand.
+
+## POSet
+
+There is a lot of classes and relation in the model.
+Most of there relations are based on posets (from the lib `poset`.)
+
+Posets are *partially-ordered sets*; they are used to modelize hierarchies of things (eg. hierarchies of modules)
+
+The poset is an expressive data structure that generalizes most services about hierarchies.
+This avoid the duplication of code and the over-specialization of services.
+The drawback is that a specific request on the model use an abstract vocabulary.
+
+Example. you want the set of modules directly imported by another module.
+There is no specific method in `MModule` for that, the good way is to use services on the some posets
+
+~~~
+var res = mymodule.in_importation.direct_greaters
+~~~
+
+posets are used in two dual ways :
+
+ - by the whole hierarchy, most are in the `Model` and are named `something_somerelation_hierarchy` (eg. `Model::mmodule_importation_hierarchy`).
+ - by the view on en entity in a specific hierarchy, they are in the `MEntity` subclasses and are name `in_somerelation`. (eg. `MModule::in_importation`).
+
+
+## Refinement
+
+The refinement is the cause of the biggest difficulty with the model since the relations between entities are relative to the module considered.
+
+"How many method in this class?" -- It depends, what modules are you considering?
+"What is called by `x.foo` when `x` is dynamically a `Bar`?" -- It depends, in which main module?
+
+This relativity cause most services on model entities to have an additional parameter for the module considered.
index 7481338..be29e6f 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Object model of the Nit language
+# Classes, types and properties
 #
-# This module define the entities of the Nit meta-model like modules,
-# classes, types and properties
-#
-# It also provide an API to build and query models.
-#
-# All model classes starts with the M letter (`MModule`, `MClass`, etc.)
-#
-# TODO: better doc
+# All three concepts are defined in this same module because these are strongly connected:
+# * types are based on classes
+# * classes contains properties
+# * some properties are types (virtual types)
 #
 # TODO: liearization, extern stuff
 # FIXME: better handling of the types
@@ -319,9 +315,20 @@ end
 #
 # This characteristic helps the reasoning about classes in a program since a
 # single `MClass` object always denote the same class.
-# However, because a `MClass` is global, it does not really have properties nor
-# belong to a hierarchy since the property and the
-# hierarchy of a class depends of a module.
+#
+# The drawback is that classes (`MClass`) contain almost nothing by themselves.
+# These do not really have properties nor belong to a hierarchy since the property and the
+# hierarchy of a class depends of the refinement in the modules.
+#
+# Most services on classes require the precision of a module, and no one can asks what are
+# the super-classes of a class nor what are properties of a class without precising what is
+# the module considered.
+#
+# For instance, during the typing of a source-file, the module considered is the module of the file.
+# eg. the question *is the method `foo` exists in the class `Bar`?* must be reformulated into
+# *is the method `foo` exists in the class `Bar` in the current module?*
+#
+# During some global analysis, the module considered may be the main module of the program.
 class MClass
        super MEntity
 
@@ -448,7 +455,14 @@ end
 #
 # A `MClassDef` is associated with an explicit (or almost) definition of a
 # class. Unlike `MClass`, a `MClassDef` is a local definition that belong to
-# a specific module
+# a specific class and a specific module, and contains declarations like super-classes
+# or properties.
+#
+# It is the class definitions that are the backbone of most things in the model:
+# ClassDefs are defined with regard with other classdefs.
+# Refinement and specialization are combined to produce a big poset called the `Model::mclassdef_hierarchy`.
+#
+# Moreover, the extension and the intention of types is defined by looking at the MClassDefs.
 class MClassDef
        super MEntity
 
@@ -769,7 +783,7 @@ abstract class MType
 
        # Replace formals generic types in self with resolved values in `mtype`
        # If `cleanup_virtual` is true, then virtual types are also replaced
-       # with their bounds
+       # with their bounds.
        #
        # This function returns self if `need_anchor` is false.
        #
@@ -830,8 +844,6 @@ abstract class MType
        #
        # The resolution can be done because `E` make sense for the class A (see `can_resolve_for`)
        #
-       # TODO: Explain the cleanup_virtual
-       #
        # FIXME: the parameter `cleanup_virtual` is just a bad idea, but having
        # two function instead of one seems also to be a bad idea.
        #
@@ -1199,8 +1211,8 @@ end
 # It's mean that all refinements of a same class "share" the parameter type,
 # but that a generic subclass has its on parameter types.
 #
-# However, in the sense of the meta-model, the a parameter type of a class is
-# a valid types in a subclass. The "in the sense of the meta-model" is
+# However, in the sense of the meta-model, a parameter type of a class is
+# a valid type in a subclass. The "in the sense of the meta-model" is
 # important because, in the Nit language, the programmer cannot refers
 # directly to the parameter types of the super-classes.
 #
diff --git a/src/parser/README b/src/parser/README
deleted file mode 100644 (file)
index 981578a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-This directory contains the nit parser. It is generated from a grammar for sablecc3 ( http://www.sablecc.org ).
-In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
-
-Contents:
-
-       fact_parser.pl: Script used to factorize parser.nit
-       Makefile: Update grammar and generate .nit files
-       nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
-       prescc.pl: Program to transform an extended sablecc3 to a standard one
-       parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
-       tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
-       test_parser.nit:
-       xss/*.xss: alternate SableCC3 template files for the Nit language
-
-
-The following are generated but present to avoid the need of sablecc3:
-
-       lexer.nit: generated lexer
-       parser.nit: generated parser
-       parser_prod.nit: All production with generated visit methods
-       tables_nit.c: The tables needed by the parser and the lexer
-       parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
-
-
-Other temp files produced by the Makefile:
-
-       .nit.sablecc3: Sablecc3 grammar after processing
-       .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
-       .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
-
diff --git a/src/parser/README.md b/src/parser/README.md
new file mode 100644 (file)
index 0000000..3595aa7
--- /dev/null
@@ -0,0 +1,42 @@
+Parser and AST for the Nit language
+
+The parser ans the AST are mostly used by all tools.
+
+The `parser` is the tool that transform source-files into abstract syntax trees (AST) (see `parser_nodes`)
+While the AST is a higher abstraction than blob of text, the AST is still limited and represents only *What the programmer says*.
+
+Classes of nodes of the AST starts with the letter `A` (for most things, eg. `AClassdef`) or `T` (for token eg. `TId`), there is no real reason except historical that might be solved with a new parser.
+
+Variable names of the AST usually starts with `n` (for node). This is also historical but some names with a `a` (to mimic the class name) remains.
+
+## SableCC
+
+Most files in this directory are generated from a grammar for sablecc3 ( http://www.sablecc.org ).
+In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
+
+## Contents
+
+* fact_parser.pl: Script used to factorize parser.nit
+* Makefile: Update grammar and generate .nit files
+* nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
+* prescc.pl: Program to transform an extended sablecc3 to a standard one
+* parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
+* tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
+* xss/*.xss: alternate SableCC3 template files for the Nit language
+
+
+The following are generated but present to avoid the need of sablecc3:
+
+* lexer.nit: generated lexer
+* parser.nit: generated parser
+* parser_prod.nit: All production with generated visit methods
+* tables_nit.c: The tables needed by the parser and the lexer
+* parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
+
+
+Other temp files produced by the Makefile:
+
+* .nit.sablecc3: Sablecc3 grammar after processing
+* .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
+* .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
+
index 3c5367f..9e3e92e 100644 (file)
@@ -19,6 +19,7 @@ module platform
 import modelize_property
 import parser_util
 import modelbuilder
+private import annotation
 
 redef class ToolContext
        var platform_phase: Phase = new PlatformPhase(self, [modelize_property_phase])
@@ -37,7 +38,7 @@ private class PlatformPhase
                var annotation_name = "platform"
 
                # Skip if we are not interested
-               if nat.n_atid.n_id.text != annotation_name then return
+               if nat.name != annotation_name then return
 
                # Do some validity checks and print errors if the annotation is used incorrectly
                var modelbuilder = toolcontext.modelbuilder
@@ -54,21 +55,12 @@ private class PlatformPhase
                else if args.is_empty then
                        platform_name = nmoduledecl.n_name.collect_text
                else
-                       var arg = args.first
-                       var format_error = "Syntax error: \"{annotation_name}\" expects its argument to be the name of the target platform as a String literal."
-                       
-                       if not arg isa AExprAtArg then
+                       platform_name = args.first.as_string
+                       if platform_name == null then
+                               var format_error = "Syntax error: \"{annotation_name}\" expects its argument to be the name of the target platform as a String literal."
                                modelbuilder.error(nat, format_error)
                                return
                        end
-
-                       var expr = arg.n_expr
-                       if not expr isa AStringFormExpr then
-                               modelbuilder.error(nat, format_error)
-                               return
-                       end
-                       var target = expr.collect_text
-                       platform_name = target.substring(1, target.length-2)
                end
 
                var nmodule = nmoduledecl.parent.as(AModule)
index 8d1b008..bf3cb12 100644 (file)
@@ -105,6 +105,7 @@ redef class ModelBuilder
 
                # The main function of the C
                compiler.new_file("{mainmodule.name}.main")
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # compile methods
@@ -870,9 +871,17 @@ class SeparateCompiler
 
        redef fun compile_nitni_structs
        do
-               self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       struct instance *value;
+\};
+"""
+               super
        end
-       
+
        redef fun finalize_ffi_for_module(mmodule)
        do
                var old_module = self.mainmodule
index 126cde0..a60cccd 100644 (file)
@@ -71,6 +71,7 @@ redef class ModelBuilder
 
                # The main function of the C
                compiler.new_file("{mainmodule.name}.main")
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # compile methods
index 0cb7961..73d8683 100644 (file)
@@ -153,6 +153,7 @@ class VirtualMachine super NaiveInterpreter
                for(i=0; i<size; i++)
                        attributes[i] = null_instance;
 
+               Instance_incr_ref(null_instance);
                return attributes;
        `}
 
@@ -199,9 +200,6 @@ class VirtualMachine super NaiveInterpreter
 
                var id = mproperty.intro_mclassdef.mclass.vtable.id
                
-               # TODO : ugly hack
-               recv.attributes[mproperty] = value
-
                # Replace the old value of mproperty in recv
                write_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
                                        recv.vtable.mask, id, mproperty.offset, value)
@@ -223,6 +221,7 @@ class VirtualMachine super NaiveInterpreter
                int absolute_offset = *(pointer + 1);
 
                ((Instance *)instance)[absolute_offset + offset] = value;
+               Instance_incr_ref(value);
        `}
 end
 
@@ -438,4 +437,4 @@ class MemoryManager
 
                return vtable;
        `}
-end
\ No newline at end of file
+end
diff --git a/tests/sav/fixme/nitvm_args3.res b/tests/sav/fixme/nitvm_args3.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/test_sqlite3_nity.res b/tests/sav/test_sqlite3_nity.res
new file mode 100644 (file)
index 0000000..4101597
--- /dev/null
@@ -0,0 +1,12 @@
+####
+uname: Bob
+pass: zzz
+activated: 1
+perc: 77.7
+####
+uname: Guillaume
+pass: xxx
+activated: 1
+perc: 88.8
+true
+false
diff --git a/tests/sav/test_sqlite3_nity_alt1.res b/tests/sav/test_sqlite3_nity_alt1.res
new file mode 100644 (file)
index 0000000..4aa3a13
--- /dev/null
@@ -0,0 +1,2 @@
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
+unable to open database file
diff --git a/tests/sav/test_sqlite3_nity_alt2.res b/tests/sav/test_sqlite3_nity_alt2.res
new file mode 100644 (file)
index 0000000..8d75750
--- /dev/null
@@ -0,0 +1,2 @@
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
+SQL logic error or missing database
diff --git a/tests/test_ffi_c_lots_of_refs.nit b/tests/test_ffi_c_lots_of_refs.nit
new file mode 100644 (file)
index 0000000..79c2654
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+extern class As `{ A* `}
+       new (size: Int) `{
+               return malloc(sizeof(A) * size);
+       `}
+
+       fun []=(i: Int, v: A) `{
+               recv[i] = v;
+               A_incr_ref(v);
+       `}
+
+       fun [](i: Int): A `{
+               return recv[i];
+       `}
+end
+
+class A
+       var i: Int
+       var s: String
+
+       init(i: Int)
+       do
+               self.i = i
+               self.s = i.to_s
+       end
+
+       redef fun to_s do return "<{i} {s}>"
+end
+
+var length = 1000000
+var aaa = new As(length)
+for i in length.times do aaa[i] = new A(i)
+sys.force_garbage_collection
+for i in length.times do assert aaa[i].to_s == "<{i} {i}>"
similarity index 89%
rename from tests/test_sqlite3.nit
rename to tests/test_sqlite3_native.nit
index 6ddb8a7..614619e 100644 (file)
@@ -15,9 +15,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-module test_sqlite3
+module test_sqlite3_native
 
-import sqlite3
+import sqlite3::native_sqlite3
 
 var filename = "test.db"
 filename.file_delete
@@ -26,7 +26,7 @@ var insert_req_1 = "INSERT INTO users VALUES('Bob', 'zzz', 1)"
 var insert_req_2 = "INSERT INTO users VALUES('Guillaume', 'xxx', 1)"
 var select_req = "SELECT * FROM users"
 
-var db = new Sqlite3.open(filename)
+var db = new NativeSqlite3.open(filename)
 assert sqlite_open: db.error.is_ok
 
 db.exec(create_req)
@@ -53,11 +53,11 @@ end
 
 db.close
 
-db = new Sqlite3.open(filename)
+db = new NativeSqlite3.open(filename)
 assert sqlite_reopen: db.error.is_ok
 
 stmt = db.prepare(select_req)
 assert sqlite_reselect: db.error.is_ok
 assert stmt != null
 stmt.step
-assert sqlite_column_0_0_reopened: stmt.column_text(0) == "Bob"
+assert sqlite_column_0_0_reopened: stmt.column_text(0).to_s == "Bob"
diff --git a/tests/test_sqlite3_nity.nit b/tests/test_sqlite3_nity.nit
new file mode 100644 (file)
index 0000000..ea09f8c
--- /dev/null
@@ -0,0 +1,63 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Guillaume Auger <jeho@resist.ca>
+# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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 sqlite3
+
+var path = "test_nity.db"
+#alt1#path = "/../invalid_path.db"
+if path.file_exists then path.file_delete
+
+var db = new Sqlite3DB.open(path)
+assert db.is_open else print db.error or else "no error?"
+
+assert db.create_table("IF NOT EXISTS users (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else
+       print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Bob', 'zzz', 1, 77.7)") else
+       print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Guillaume', 'xxx', 1, 88.8)") else
+       print db.error or else "no error?"
+end
+
+#alt2#assert db.insert("INTO notable VALUES('Alexis', 'asdf', 2, 99.9)") else
+#alt2# print db.error or else "no error?"
+#alt2#end
+
+for row in db.select("* FROM users") do
+       print "####"
+
+       printn "{row[0].name}: "
+       print row[0]
+
+       printn "{row[1].name}: "
+       var val = row[1].value
+       assert val isa Text
+       print val.to_s
+
+       printn "{row[2].name}: "
+       print row[2].to_i
+
+       printn "{row[3].name}: "
+       print row[3].to_f
+end
+
+print db.is_open
+db.close
+print db.is_open