Merge: Java FFI: use class NitObject for references to Nit objects from Java
authorJean Privat <jean@pryen.org>
Fri, 1 Mar 2019 00:08:44 +0000 (19:08 -0500)
committerJean Privat <jean@pryen.org>
Fri, 1 Mar 2019 00:08:44 +0000 (19:08 -0500)
This PR improves the type safety of the Java FFI and fixes a bug with pointers to Nit objects at the same time.

* The main change is the introduction of the Java class `NitObject` used as a general type for Nit objects referenced from Java code. It replaces the use of a simple `int` holding the pointer value, which was the source of the Java FFI bug.

* Change internal pointers to Nit object to use the Java primitive types `long` instead of `int`, addressing to the same pointer bug. Only low-level services should still use `long` this way, in this case it is the support for Android that relies mostly on the FFI with C.

* Change the annotation `extra_java_files` to accept the full name of Java classes (package + class) instead of the path to the Java source file. The compile still looks for the file in the same directory as the Nit module.

* Clear up the documentation of the class `ExternFile` for `filename` to be relative to the compilation folder. This required updating previous usages.

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

54 files changed:
.gitlab-ci.yml
benchmarks/bench_engines.sh
contrib/benitlux/src/client/android.nit
contrib/nitrpg/src/game.nit
contrib/nitrpg/src/test_helper.nit
contrib/nitrpg/src/web.nit
lib/android/NitActivity.java
lib/android/README.md
lib/android/activities.nit
lib/android/assets_and_resources.nit
lib/android/bundle/bundle.nit
lib/android/intent/intent_api10.nit
lib/android/nit_activity.nit
lib/android/service/NitService.java
lib/android/service/at_boot.nit
lib/android/service/service.nit
lib/android/shared_preferences/shared_preferences_api10.nit
lib/android/shared_preferences/shared_preferences_api11.nit
lib/android/ui/ui.nit
lib/curl/curl.nit
lib/curl/extra.nit [new file with mode: 0644]
lib/github/loader.nit
lib/java/NitObject.java [new file with mode: 0644]
lib/java/ffi_support.nit
lib/mongodb/mongodb.nit
lib/neo4j/neo4j.nit
lib/popcorn/README.md
lib/popcorn/examples/mongodb/example_mongodb.nit
lib/popcorn/pop_auth.nit
lib/popcorn/pop_repos.nit
lib/postgresql/postgres.nit
misc/docker/ci-local/README.md [new file with mode: 0644]
misc/docker/ci-local/docker-compose.yml [new file with mode: 0644]
misc/docker/ci/Dockerfile
misc/jenkins/checksignedoffby.sh
share/android-bdwgc/setup.sh
src/c_tools.nit
src/compiler/abstract_compiler.nit
src/compiler/compiler_ffi/light.nit
src/ffi/cpp.nit
src/ffi/extra_java_files.nit
src/ffi/java.nit
src/ffi/light_ffi.nit
src/ffi/objc.nit
src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit
src/platform/android.nit
tests/gitlab_ci.skip
tests/test_curl.nit
tests/test_ffi_java_annot_files.nit
tests/test_neo.args
tests/test_neo4j.nit
tests/test_neo4j_batch.nit
tests/test_postgres_native.nit
tests/test_postgres_nity.nit

index 1a70ece..7bb7ada 100644 (file)
@@ -1,8 +1,18 @@
 image: nitlang/nit-ci
 
+services:
+  - mongo
+  - neo4j:2.3
+  - postgres
+
+variables:
+  NEO4J_AUTH: none
+
 cache:
   paths:
     - .ccache
+    - .gradle/caches
+    - .gradle/wrapper
   key: "$CI_JOB_NAME"
 
 stages:
@@ -14,16 +24,20 @@ stages:
 before_script:
   - date
   - export CCACHE_DIR=$PWD/.ccache
+  - export GRADLE_USER_HOME=$PWD/.gradle
   - export PATH=$PWD/bin:$PATH
+  - git config --add github.oauthtoken "$GITHUB_OAUTHTOKEN" # needed for github api rate limit
   - pwd
   - ccache -s
   - ccache -M 500M
+  - du -sh .gradle || true
   - type -a nitc nitdoc || true # is there some nit tools?
   - "> status.txt"
 
 after_script:
   - export CCACHE_DIR=$PWD/.ccache
   - ccache -s
+  - du -sh .gradle || true
   - git status --ignored
   - date
   - tail status.txt
@@ -43,21 +57,13 @@ build_tools:
     - make 2>> status.txt
     - nitc --version
     - misc/jenkins/check_manpages.sh
-    - "{ cd tests && ./tests.sh base_sim*.nit ../src/nitlight.nit; }"
-    - nitunit -v lib/core
   artifacts:
     paths:
       - bin/*
       - c_src/nitc
       - src/version.nit
       - src/nitc_0
-      - nitunit.xml*
-      - tests/*.xml*
     when: always
-    reports:
-      junit:
-        - tests/*.xml
-        - nitunit.xml
 
 test_some:
   stage: test
@@ -81,7 +87,7 @@ nitunit_some:
   dependencies:
     - build_tools
   script:
-    - git diff --name-only origin/master..HEAD -- "*.nit" "*.res" "README.*" | grep -v "^tests/" > list0.txt
+    - git diff --name-only origin/master..HEAD -- "*.nit" "*.res" "README.*" | grep -v "^tests/" > list0.txt || true
     - xargs nitls -pP < list0.txt > list.txt
     - xargs nitunit < list.txt
   artifacts:
@@ -98,9 +104,19 @@ nitpick_full:
     - build_tools
   script:
     - nitls lib src examples contrib
-    - nitls -Pp lib src examples | grep -v -f tests/gitlab_ci.skip > list.txt # filter what is skipped by tests.sh
+    - nitls -Pp lib src examples | grep -v -f tests/gitlab_ci.skip > list.txt || true # filter what is skipped by tests.sh
     - xargs nitpick < list.txt
 
+basic_android:
+  stage: test
+  dependencies:
+    - build_tools
+  script:
+    - make -C contrib/asteronits android
+  artifacts:
+    paths:
+      - contrib/asteronits/bin/*.apk
+
 # TEST FULL #########################################################
 
 test_full_nitcs:
@@ -200,7 +216,7 @@ nitunit_lib:
   dependencies:
     - build_tools
   script:
-    - nitls -Pp lib | grep -v -f tests/gitlab_ci.skip > list.txt # filter what is skipped by tests.sh
+    - nitls -Pp lib | grep -v -f tests/gitlab_ci.skip > list.txt || true # filter what is skipped by tests.sh
     - xargs nitunit -v < list.txt| tee log.txt
     - grep -e KO log.txt > status.txt || true
     - tail -3 log.txt >> status.txt
@@ -217,7 +233,7 @@ nitunit_src:
   dependencies:
     - build_tools
   script:
-    - nitls -Pp src examples | grep -v -f tests/gitlab_ci.skip > list.txt # filter what is skipped by tests.sh
+    - nitls -Pp src examples | grep -v -f tests/gitlab_ci.skip > list.txt || true # filter what is skipped by tests.sh
     - xargs nitunit -v < list.txt| tee log.txt
     - grep -e KO log.txt > status.txt || true
     - tail -3 log.txt >> status.txt
@@ -237,7 +253,26 @@ test_contribs:
     - misc/jenkins/check_contrib.sh all check
     - grep 'error message' *.xml > status.txt || true
     - test ! -s status.txt # no lines, no errors
-  allow_failure: true
+
+test_contribs_android:
+  stage: more_test
+  dependencies:
+    - build_tools
+  script:
+    - misc/jenkins/check_contrib.sh android
+    - grep 'error message' *.xml > status.txt || true
+    - mkdir -p apk/debug
+    - find . -name '*.apk' -exec mv {} apk/debug/ ";"
+    - test ! -s status.txt # no lines, no errors
+    - misc/jenkins/check_contrib.sh android-release
+    - grep 'error message' *.xml > status.txt || true
+    - mkdir -p apk/release
+    - find . -name '*.apk' -exec mv {} apk/release ";"
+    - test ! -s status.txt # no lines, no errors
+  artifacts:
+    paths:
+      - "apk"
+    when: always
 
 build_oot:
   stage: more_test
@@ -247,8 +282,7 @@ build_oot:
     - cd contrib
     - ./oot.sh all
     - grep 'error message' *.xml > ../status.txt || true
-    - test ! -s ../status.txt # no lines, no errors
-  allow_failure: true
+    # Errors are somewhat expected
 
 # MISC ##############################################################
 
@@ -276,7 +310,6 @@ bench_fast:
       - benchmarks/*.dat
       - benchmarks/*.gnu
     when: always
-  allow_failure: true
 
 
 # MORE TOOLS ########################################################
index dfe66ae..ad25612 100755 (executable)
@@ -217,7 +217,8 @@ bench_nitc_options "faster" --erasure --skip-dead-methods --inline-coloring-numb
 bench_nitc_options "engine" "" NOALL "--separate" "--erasure" "--separate --semi-global" "--erasure --semi-global" "--erasure --semi-global --rta" "--global"
 bench_nitc_options "policy" "" NOALL "--separate" "--erasure" "--separate --no-check-covariance" "--erasure --no-check-covariance --no-check-erasure-cast"
 bench_nitc_options "nullables" "" "--no-check-attr-isset" "--no-union-attribute"
-bench_nitc_options "linkboost" "" NOALL --trampoline-call --colors-are-symbols "--colors-are-symbols --trampoline-call" "--separate --link-boost" "--separate --colors-are-symbols --guard-call" "--separate --colors-are-symbols --direct-call-monomorph0" "--substitute-monomorph"
+#bench_nitc_options "linkboost" "" NOALL --trampoline-call --colors-are-symbols "--colors-are-symbols --trampoline-call" "--separate --link-boost" "--separate --colors-are-symbols --guard-call" "--separate --colors-are-symbols --direct-call-monomorph0" "--substitute-monomorph" # --colors-are-symbols is broken :(
+bench_nitc_options "linkboost" "" NOALL --trampoline-call --guard-call --substitute-monomorph
 bench_nitc_options "monomorph" "" --direct-call-monomorph0 --direct-call-monomorph
 
 bench_nitc_options "misc" "" --log --typing-test-metrics --invocation-metrics --isset-checks-metrics --tables-metrics --no-stacktrace --release --debug #FIXME add --sloppy
index ee51327..3495f6c 100644 (file)
@@ -52,7 +52,7 @@ redef class App
 
                android.content.IntentFilter filter = new android.content.IntentFilter();
                filter.addAction(android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-               final int final_self = self;
+               final nit.app.NitObject final_self = self;
                App_incr_ref(final_self);
 
                context.registerReceiver(
@@ -197,7 +197,7 @@ redef class BeerView
                final String final_title = title;
                final boolean final_loggedin = loggedin;
 
-               final int final_self = self;
+               final nit.app.NitObject final_self = self;
                BeerView_incr_ref(self); // Nit GC
 
                view.setOnTouchListener(new android.view.View.OnTouchListener() {
index b1d8916..5413f43 100644 (file)
@@ -75,7 +75,7 @@ class Game
        redef var key = name is lazy
 
        # Mongo server url where this game data are stored.
-       var mongo_url = "mongodb://localhost:27017" is writable
+       var mongo_url = "mongodb://mongo:27017" is writable
 
        # Mongo db client.
        var client = new MongoClient(mongo_url) is lazy
index 919ccfc..9d71ca3 100644 (file)
@@ -31,7 +31,7 @@ abstract class NitrpgTestHelper
        end
 
        # Mongo API client
-       var mongo = new MongoClient("mongodb://localhost:27017/")
+       var mongo = new MongoClient("mongodb://mongo:27017/")
 
        # Load a new test database by with a name
        private fun load_db(name: String): MongoDb do return mongo.database(name)
index c563af7..3d54672 100644 (file)
@@ -67,7 +67,7 @@ class RpgAction
        fun load_games: Array[Game] do
                var res = new Array[Game]
                # TODO should be option
-               var mongo = new MongoClient("mongodb://localhost:27017")
+               var mongo = new MongoClient("mongodb://mongo:27017")
                var db = mongo.database("nitrpg")
                for obj in db.collection("games").find_all(new JsonObject) do
                        var repo = api.load_repo(obj["name"].to_s)
index 2f712f1..ca15d38 100644 (file)
@@ -25,7 +25,7 @@ import android.view.KeyEvent;
 public class NitActivity extends Activity {
 
        // Nit activity associated to `this`
-       protected int nitActivity = 0;
+       protected long nitActivity = 0;
 
        /*
         * Calls to Nit or to the C framework
@@ -38,21 +38,21 @@ public class NitActivity extends Activity {
        /*
         * Callbacks to Nit through C
         */
-       protected native int nitRegisterActivity();
-       protected native void nitOnCreate(int activity, Bundle savedInstanceState);
-       protected native void nitOnStart(int activity);
-       protected native void nitOnRestart(int activity);
-       protected native void nitOnResume(int activity);
-       protected native void nitOnPause(int activity);
-       protected native void nitOnStop(int activity);
-       protected native void nitOnDestroy(int activity);
-       protected native void nitOnSaveInstanceState(int activity, Bundle savedInstanceState);
-       protected native void nitOnRestoreInstanceState(int activity, Bundle savedInstanceState);
-       protected native boolean nitOnBackPressed(int activity);
-       protected native boolean nitOnKeyDown(int activity, int keyCode, KeyEvent event);
-       protected native boolean nitOnKeyLongPress(int activity, int keyCode, KeyEvent event);
-       protected native boolean nitOnKeyMultiple(int activity, int keyCode, int count, KeyEvent event);
-       protected native boolean nitOnKeyUp(int activity, int keyCode, KeyEvent event);
+       protected native long nitRegisterActivity();
+       protected native void nitOnCreate(long activity, Bundle savedInstanceState);
+       protected native void nitOnStart(long activity);
+       protected native void nitOnRestart(long activity);
+       protected native void nitOnResume(long activity);
+       protected native void nitOnPause(long activity);
+       protected native void nitOnStop(long activity);
+       protected native void nitOnDestroy(long activity);
+       protected native void nitOnSaveInstanceState(long activity, Bundle savedInstanceState);
+       protected native void nitOnRestoreInstanceState(long activity, Bundle savedInstanceState);
+       protected native boolean nitOnBackPressed(long activity);
+       protected native boolean nitOnKeyDown(long activity, int keyCode, KeyEvent event);
+       protected native boolean nitOnKeyLongPress(long activity, int keyCode, KeyEvent event);
+       protected native boolean nitOnKeyMultiple(long activity, int keyCode, int count, KeyEvent event);
+       protected native boolean nitOnKeyUp(long activity, int keyCode, KeyEvent event);
 
        /*
         * Implementation of OS callbacks
index ffd961c..c948c48 100644 (file)
@@ -23,33 +23,33 @@ it may be possible to support other platforms with some tweaks.
                You will probably need to tweak it to you system or update the download URL
                to the latest SDK tools from https://developer.android.com/studio/index.html#command-tools
 
-               ~~~
-               # Fetch and extract SDK tools
-               mkdir -p ~/Android/Sdk
-               cd ~/Android/Sdk
-               wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
-               unzip sdk-tools-linux-3859397.zip
+       ~~~raw
+       # Fetch and extract SDK tools
+       mkdir -p ~/Android/Sdk
+       cd ~/Android/Sdk
+       wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
+       unzip sdk-tools-linux-3859397.zip
 
-               # Update tools
-               tools/bin/sdkmanager --update
+       # Update tools
+       tools/bin/sdkmanager --update
 
-               # Accept the licenses
-               tools/bin/sdkmanager --licenses
+       # Accept the licenses
+       tools/bin/sdkmanager --licenses
 
-               # Install the basic build tools
-               tools/bin/sdkmanager "build-tools;27.0.0" ndk-bundle
-               ~~~
+       # Install the basic build tools
+       tools/bin/sdkmanager "build-tools;27.0.0" ndk-bundle
+       ~~~
 
 3.     Set the environment variable ANDROID_HOME to the SDK installation directory, usually `~/Android/Sdk/`.
        Use the following command to setup the variable for bash.
 
-       ~~~
+       ~~~raw
        echo "export ANDROID_HOME=~/Android/Sdk/" >> ~/.bashrc
        ~~~
 
 4.     Install Java 8 JDK, on Debian/Ubuntu systems you can use the following command:
 
-       ~~~
+       ~~~raw
        sudo apt install openjdk-8-jdk
        ~~~
 
@@ -71,7 +71,7 @@ and `android_manifest_activity`.
 
     Example usage to specify an extra permission:
 
-    ~~~
+    ~~~raw
     android_manifest """<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>"""
     ~~~
 
@@ -143,7 +143,7 @@ as they change between versions of the Java SDK. You should instead use a
 command similar to the following, replacing `KEYSTORE_PATH` and `KEY_ALIAS`
 with the desired values.
 
-    ~~~
+    ~~~raw
     keytool -genkey -keystore KEYSTORE_PATH -alias KEY_ALIAS -sigalg MD5withRSA -keyalg RSA -keysize 1024 -validity 10000
     ~~~
 
@@ -154,7 +154,7 @@ optionally `TSA_SERVER`. These settings can be set in a startup script such as
     You can use the following commands by replacing the right-hand values
 to your own configuration.
 
-    ~~~
+    ~~~raw
     export KEYSTORE=keystore_path
     export KEY_ALIAS=key_alias
     export TSA_SERVER=timestamp_authority_server_url # Optional
index a457899..76e3f07 100644 (file)
@@ -43,7 +43,7 @@ extern class NativeActivity in "Java" `{ android.app.Activity `}
 
        # Execute `task.main` on the UI thread when possible
        fun run_on_ui_thread(task: Task) import Task.main in "Java" `{
-               final int final_task = task;
+               final nit.app.NitObject final_task = task;
                Runnable runnable = new Runnable() {
                        @Override
                        public void run() {
index 930bc17..2ca56d4 100644 (file)
@@ -52,7 +52,7 @@ private extern class NativeAssetManager in "Java" `{ android.content.res.AssetMa
 
        # Get the locales that this assets manager contains data for
        fun get_locales: Array[JavaString] import Array[JavaString], Array[JavaString].add in "Java" `{
-               int arr = new_Array_of_JavaString();
+               nit.app.NitObject arr = new_Array_of_JavaString();
                for (String s : self.getLocales()) {
                        Array_of_JavaString_add(arr, s);
                }
@@ -61,7 +61,7 @@ private extern class NativeAssetManager in "Java" `{ android.content.res.AssetMa
 
        # String Array of all the assets at the given path
        fun list(path: JavaString): Array[JavaString] import Array[JavaString], Array[JavaString].add  in "Java" `{
-               int arr = new_Array_of_JavaString();
+               nit.app.NitObject arr = new_Array_of_JavaString();
                try {
                        for (String s : self.list(path)) {
                                Array_of_JavaString_add(arr, s);
index e02702a..521355f 100644 (file)
@@ -29,6 +29,7 @@ in "Java" `{
        import android.app.Activity;
        import java.util.ArrayList;
        import java.util.Set;
+       import nit.app.NitObject;
 `}
 
 extern class NativeBundle in "Java" `{ android.os.Bundle `}
@@ -44,10 +45,10 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get(key: JavaString): JavaObject in "Java" `{ return self.get(key); `}
        fun remove(key: JavaString) in "Java" `{ self.remove(key); `}
        fun put_all(bundle: NativeBundle) in "Java" `{ self.putAll(bundle); `}
-       fun key_set: HashSet[JavaString] import HashSet[JavaString], 
-         HashSet[JavaString].add in "Java" `{ 
+       fun key_set: HashSet[JavaString] import HashSet[JavaString],
+         HashSet[JavaString].add in "Java" `{
                Set<String> java_set = self.keySet();
-               int nit_hashset = new_HashSet_of_JavaString();
+               NitObject nit_hashset = new_HashSet_of_JavaString();
 
                for (String element: java_set)
                        HashSet_of_JavaString_add(nit_hashset, element);
@@ -257,7 +258,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_integer_array_list(key: JavaString): Array[Int]
                import Array[Int], Array[Int].add in "Java" `{
                ArrayList<Integer> java_array = self.getIntegerArrayList(key);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                if (java_array == null) return nit_array;
 
@@ -269,7 +270,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_string_array_list(key: JavaString): Array[String]
                import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                ArrayList<String> java_array = self.getStringArrayList(key);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                if (java_array == null) return nit_array;
 
@@ -281,7 +282,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_char_sequence_array_list(key: JavaString): Array[String]
                import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                ArrayList<CharSequence> java_array = self.getCharSequenceArrayList(key);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                if (java_array == null) return nit_array;
 
@@ -293,7 +294,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_boolean_array(key: JavaString): Array[Bool]
                import Array[Bool], Array[Bool].add in "Java" `{
                boolean[] java_array = self.getBooleanArray(key);
-               int nit_array = new_Array_of_Bool();
+               NitObject nit_array = new_Array_of_Bool();
 
                if (java_array == null) return nit_array;
 
@@ -305,7 +306,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_byte_array(key: JavaString): Array[Int]
                import Array[Int], Array[Int].add in "Java" `{
                byte[] java_array = self.getByteArray(key);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                if (java_array == null) return nit_array;
 
@@ -317,7 +318,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_short_array(key: JavaString): Array[Int]
                import Array[Int], Array[Int].add in "Java" `{
                short[] java_array = self.getShortArray(key);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                if (java_array == null) return nit_array;
 
@@ -330,7 +331,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_char_array(key: JavaString): Array[Char]
                import Array[Char], Array[Char].add in "Java" `{
                char[] java_array = self.getCharArray(key);
-               int nit_array = new_Array_of_Char();
+               NitObject nit_array = new_Array_of_Char();
 
                if (java_array == null) return nit_array;
 
@@ -342,7 +343,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_int_array(key: JavaString): Array[Int]
                import Array[Int], Array[Int].add in "Java" `{
                int[] java_array = self.getIntArray(key);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                if (java_array == null) return nit_array;
 
@@ -355,7 +356,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_long_array(key: JavaString): Array[Int]
                import Array[Int], Array[Int].add in "Java" `{
                long[] java_array = self.getLongArray(key);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                if (java_array == null) return nit_array;
 
@@ -367,7 +368,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_float_array(key: JavaString): Array[Float]
                import Array[Float], Array[Float].add in "Java" `{
                float[] java_array = self.getFloatArray(key);
-               int nit_array = new_Array_of_Float();
+               NitObject nit_array = new_Array_of_Float();
 
                if (java_array == null) return nit_array;
 
@@ -379,7 +380,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_double_array(key: JavaString): Array[Float]
                import Array[Float], Array[Float].add in "Java" `{
                double[] java_array = self.getDoubleArray(key);
-               int nit_array = new_Array_of_Float();
+               NitObject nit_array = new_Array_of_Float();
 
                if (java_array == null) return nit_array;
 
@@ -391,7 +392,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_string_array(key: JavaString): Array[String]
                import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                String[] java_array = self.getStringArray(key);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                if (java_array == null) return nit_array;
 
@@ -403,7 +404,7 @@ extern class NativeBundle in "Java" `{ android.os.Bundle `}
        fun get_char_sequence_array(key: JavaString): Array[String]
                import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                CharSequence[] java_array = self.getCharSequenceArray(key);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                if (java_array == null) return nit_array;
 
index f9e6c3b..1c05f89 100644 (file)
@@ -29,6 +29,7 @@ in "Java" `{
        import android.graphics.Rect;
        import java.util.Set;
        import java.util.ArrayList;
+       import nit.app.NitObject;
 `}
 
 extern class NativeIntent in "Java" `{ android.content.Intent `}
@@ -45,7 +46,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun boolean_array_extra(name: JavaString): Array[Bool] import Array[Bool],
          Array[Bool].push in "Java" `{
                boolean[] java_array = self.getBooleanArrayExtra(name);
-               int nit_array = new_Array_of_Bool();
+               NitObject nit_array = new_Array_of_Bool();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Bool_push(nit_array, java_array[i]);
@@ -58,7 +59,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun byte_array_extra(name: JavaString): Array[Int] import Array[Int],
          Array[Int].add in "Java" `{
                byte[] java_array = self.getByteArrayExtra(name);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                for (int i=0; i < java_array.length; ++i)
                        Array_of_Int_add(nit_array, java_array[i]);
@@ -72,7 +73,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun char_array_extra(name: JavaString): Array[Char] import Array[Char],
          Array[Char].add in "Java" `{
                char[] java_array = self.getCharArrayExtra(name);
-               int nit_array = new_Array_of_Char();
+               NitObject nit_array = new_Array_of_Char();
 
                for (int i = 0; i < java_array.length; ++i)
                        Array_of_Char_add(nit_array, java_array[i]);
@@ -86,7 +87,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun char_sequence_array_extra(name: JavaString): Array[String]
          import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                CharSequence[] java_array = self.getCharSequenceArrayExtra(name);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                for (int i = 0; i < java_array.length; ++i)
                        StringCopyArray_add(nit_array, (String) java_array[i]);
@@ -96,7 +97,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun char_sequence_array_list_extra(name: JavaString): Array[String]
          import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                ArrayList<CharSequence> java_array = self.getCharSequenceArrayListExtra(name);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                if (java_array == null) return nit_array;
 
@@ -111,7 +112,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun categories: HashSet[String] import StringCopyHashSet,
          StringCopyHashSet.add, StringCopyHashSet.collection  in "Java" `{
                Set<String> java_set = self.getCategories();
-               int nit_hashset = new_StringCopyHashSet();
+               NitObject nit_hashset = new_StringCopyHashSet();
 
                if (java_set == null) return nit_hashset;
 
@@ -125,7 +126,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun double_array_extra(name: JavaString): Array[Float] import Array[Float],
          Array[Float].push in "Java" `{
                double[] java_array = self.getDoubleArrayExtra(name);
-               int nit_array = new_Array_of_Float();
+               NitObject nit_array = new_Array_of_Float();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Float_push(nit_array, java_array[i]);
@@ -139,7 +140,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun float_array_extra(name: JavaString): Array[Float] import Array[Float],
          Array[Float].push in "Java" `{
                float[] java_array = self.getFloatArrayExtra(name);
-               int nit_array = new_Array_of_Float();
+               NitObject nit_array = new_Array_of_Float();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Float_push(nit_array, java_array[i]);
@@ -152,7 +153,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun int_array_extra(name: JavaString): Array[Int] import Array[Int],
          Array[Int].push in "Java" `{
                int[] java_array = self.getIntArrayExtra(name);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Int_push(nit_array, java_array[i]);
@@ -165,7 +166,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun long_array_extra(name: JavaString): Array[Int] import Array[Int],
          Array[Int].push in "Java" `{
                long[] java_array = self.getLongArrayExtra(name);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Int_push(nit_array, (int) java_array[i]);
@@ -180,7 +181,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun short_array_extra(name: JavaString): Array[Int] import Array[Int],
          Array[Int].push in "Java" `{
                short[] java_array = self.getShortArrayExtra(name);
-               int nit_array = new_Array_of_Int();
+               NitObject nit_array = new_Array_of_Int();
 
                for(int i=0; i < java_array.length; ++i)
                        Array_of_Int_push(nit_array, (int) java_array[i]);
@@ -193,7 +194,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun string_array_extra(name: JavaString): Array[String]
          import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                String[] java_array = self.getStringArrayExtra(name);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                for(int i=0; i < java_array.length; ++i)
                        StringCopyArray_add(nit_array, java_array[i]);
@@ -203,7 +204,7 @@ extern class NativeIntent in "Java" `{ android.content.Intent `}
        fun string_array_list_extra(name: JavaString): Array[String]
          import StringCopyArray, StringCopyArray.add, StringCopyArray.collection in "Java" `{
                ArrayList<String> java_array = self.getStringArrayListExtra(name);
-               int nit_array = new_StringCopyArray();
+               NitObject nit_array = new_StringCopyArray();
 
                for (String element: java_array)
                        StringCopyArray_add(nit_array, element);
index f084958..980270c 100644 (file)
@@ -36,7 +36,7 @@
 # the Java virtual machine. For this reason, the main _must_ execute quickly,
 # on the main UI thread at least.
 module nit_activity is
-       extra_java_files "NitActivity.java"
+       extra_java_files "nit.app.NitActivity"
        android_activity "nit.app.NitActivity"
 end
 
@@ -74,94 +74,94 @@ in "C body" `{
         * Implementations of NitActivity.java native methods
         */
 
-       JNIEXPORT jint JNICALL Java_nit_app_NitActivity_nitRegisterActivity
+       JNIEXPORT jlong JNICALL Java_nit_app_NitActivity_nitRegisterActivity
          (JNIEnv *env, jobject java_activity)
        {
                Activity nit_activity = App_register_activity(global_app, java_activity);
                Activity_incr_ref(nit_activity);
-               return (jint)(void*)nit_activity;
+               return (jlong)(void*)nit_activity;
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnCreate
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jobject saved_state)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jobject saved_state)
        {
                Activity_on_create((Activity)nit_activity, saved_state);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnStart
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_start((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnRestart
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_restart((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnResume
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_resume((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnPause
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_pause((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnStop
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_stop((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnDestroy
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                Activity_on_destroy((Activity)nit_activity);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnSaveInstanceState
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jobject saved_state)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jobject saved_state)
        {
                Activity_on_save_instance_state((Activity)nit_activity, saved_state);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitActivity_nitOnRestoreInstanceState
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jobject saved_state)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jobject saved_state)
        {
                Activity_on_restore_instance_state((Activity)nit_activity, saved_state);
        }
 
        JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnBackPressed
-         (JNIEnv *env, jobject java_activity, jint nit_activity)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity)
        {
                return (jboolean)Activity_on_back_pressed((Activity)nit_activity);
        }
 
        JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyDown
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jint keyCode, jobject event)
        {
                return (jboolean)Activity_on_key_down((Activity)nit_activity, keyCode, event);
        }
 
        JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyLongPress
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jint keyCode, jobject event)
        {
                return (jboolean)Activity_on_key_long_press((Activity)nit_activity, keyCode, event);
        }
 
        JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyMultiple
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jint count, jobject event)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jint keyCode, jint count, jobject event)
        {
                return (jboolean)Activity_on_key_multiple((Activity)nit_activity, keyCode, count, event);
        }
 
        JNIEXPORT jboolean JNICALL Java_nit_app_NitActivity_nitOnKeyUp
-         (JNIEnv *env, jobject java_activity, jint nit_activity, jint keyCode, jobject event)
+         (JNIEnv *env, jobject java_activity, jlong nit_activity, jint keyCode, jobject event)
        {
                return (jboolean)Activity_on_key_up((Activity)nit_activity, keyCode, event);
        }
index b0767d6..fd7e79f 100644 (file)
@@ -22,7 +22,7 @@ import android.os.IBinder;
 // Service implemented in Nit
 public class NitService extends Service {
 
-       protected int nitService = 0;
+       protected long nitService = 0;
 
        static {
                System.loadLibrary("nit_app");
@@ -51,8 +51,8 @@ public class NitService extends Service {
                return null;
        }
 
-       protected native int nitNewService();
-       protected native int nitOnStartCommand(int nitService, Intent intent, int flags, int id);
-       protected native void nitOnCreate(int nitService);
-       protected native void nitOnDestroy(int nitService);
+       protected native long nitNewService();
+       protected native int nitOnStartCommand(long nitService, Intent intent, int flags, int id);
+       protected native void nitOnCreate(long nitService);
+       protected native void nitOnDestroy(long nitService);
 }
index a2c1868..9669fe7 100644 (file)
@@ -14,7 +14,7 @@
 
 # Import this module to launch `Service` at device boot
 module at_boot is
-       extra_java_files "NitBroadcastReceiver.java"
+       extra_java_files "nit.app.NitBroadcastReceiver"
        android_manifest """
 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 """
index 901a0e2..823d16e 100644 (file)
@@ -14,7 +14,7 @@
 
 # Android service support for _app.nit_ centered around the class `Service`
 module service is
-       extra_java_files "NitService.java"
+       extra_java_files "nit.app.NitService"
        android_manifest_application """<service android:name="nit.app.NitService"></service>"""
 end
 
@@ -24,7 +24,7 @@ in "C" `{
        // Nit's App running instance, declared in `nit_activity`
        extern App global_app;
 
-       JNIEXPORT jint JNICALL Java_nit_app_NitService_nitNewService
+       JNIEXPORT jlong JNICALL Java_nit_app_NitService_nitNewService
                (JNIEnv *env, jobject java_service)
        {
                // Pin a ref to the Java service in the Java GC
@@ -39,23 +39,23 @@ in "C" `{
 
                Service_incr_ref(nit_service);
 
-               return (jint)(void*)nit_service;
+               return (jlong)(void*)nit_service;
        }
 
        JNIEXPORT jint JNICALL Java_nit_app_NitService_nitOnStartCommand
-               (JNIEnv *env, jobject java_service, jint nit_service, jobject intent, jint flags, jint id)
+               (JNIEnv *env, jobject java_service, jlong nit_service, jobject intent, jint flags, jint id)
        {
                return Service_on_start_command((Service)nit_service, intent, flags, id);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitService_nitOnCreate
-               (JNIEnv *env, jobject java_service, jint nit_service)
+               (JNIEnv *env, jobject java_service, jlong nit_service)
        {
                Service_on_create((Service)nit_service);
        }
 
        JNIEXPORT void JNICALL Java_nit_app_NitService_nitOnDestroy
-               (JNIEnv *env, jobject java_service, jint nit_service)
+               (JNIEnv *env, jobject java_service, jlong nit_service)
        {
                Service_on_destroy((Service)nit_service);
 
index 0464b67..f012ed2 100644 (file)
@@ -38,7 +38,7 @@ extern class NativeSharedPreferences in "Java" `{ android.content.SharedPreferen
        fun get_all: HashMap[JavaString, JavaObject] import HashMap[JavaString, JavaObject],
                HashMap[JavaString, JavaObject].[]= in "Java" `{
                Map<String, ?> java_map = null;
-               int nit_hashmap = new_HashMap_of_JavaString_JavaObject();
+               nit.app.NitObject nit_hashmap = new_HashMap_of_JavaString_JavaObject();
                try {
                        java_map = self.getAll();
                } catch (NullPointerException e) {
index 85623d8..9d2dbcf 100644 (file)
@@ -32,7 +32,7 @@ redef extern class NativeSharedPreferences
                HashSet[JavaString].add in "Java" `{
                Set<String> def_value = new HashSet<String>();
                Set<String> java_set = self.getStringSet(key, def_value);
-               int nit_hashset = new_HashSet_of_JavaString();
+               nit.app.NitObject nit_hashset = new_HashSet_of_JavaString();
 
                for (String element: java_set)
                        HashSet_of_JavaString_add(nit_hashset, element);
@@ -47,7 +47,7 @@ redef extern class NativeSharedPreferencesEditor
                import HashSet[JavaString], HashSet[JavaString].iterator, Iterator[JavaString].is_ok,
                Iterator[JavaString].item, Iterator[JavaString].next in "Java" `{
                Set<String> java_set = new HashSet<String>();
-               int itr = HashSet_of_JavaString_iterator(value);
+               nit.app.NitObject itr = HashSet_of_JavaString_iterator(value);
 
                while (Iterator_of_JavaString_is_ok(itr)) {
                        java_set.add(Iterator_of_JavaString_item(itr));
index 60f7909..767039b 100644 (file)
@@ -207,7 +207,7 @@ end
 redef class Android_widget_ArrayAdapter
        private new (context: NativeContext, res: Int, sender: ListLayout)
        import ListLayout.create_view in "Java" `{
-               final int final_sender_object = sender;
+               final nit.app.NitObject final_sender_object = sender;
                ListLayout_incr_ref(sender);
 
                return new android.widget.ArrayAdapter(context, (int)res) {
@@ -276,7 +276,7 @@ redef class CheckBox
 
        private fun set_callback_on_toggle(view: NATIVE)
        import on_toggle in "Java" `{
-               final int final_sender_object = self;
+               final nit.app.NitObject final_sender_object = self;
                CheckBox_incr_ref(final_sender_object);
 
                view.setOnCheckedChangeListener(
@@ -328,7 +328,7 @@ end
 redef class NativeButton
        private new (context: NativeActivity, sender_object: Button)
        import Button.on_click in "Java" `{
-               final int final_sender_object = sender_object;
+               final nit.app.NitObject final_sender_object = sender_object;
                Button_incr_ref(final_sender_object);
 
                return new android.widget.Button(context) {
@@ -349,7 +349,7 @@ end
 redef class Android_app_Fragment
        private new (nit_window: Window)
        import Window.on_create_fragment in "Java" `{
-               final int final_nit_window = nit_window;
+               final nit.app.NitObject final_nit_window = nit_window;
                Window_incr_ref(nit_window);
 
                return new android.app.Fragment(){
index 34b2576..18f05c8 100644 (file)
 
 # Data transfer powered by the native curl library
 #
-# Download or upload over HTTP with `CurlHTTPRequest` and send emails with `CurlMail`.
+# Download or upload data over HTTP with `CurlHTTPRequest` and send emails
+# with `CurlMail`. Scripts can use the easier (but limited) services on `Text`,
+# `http_get` and `http_download`, provided by `curl::extra`.
 module curl
 
 import native_curl
 
-redef class Sys
-       # Shared Curl library handle
-       #
-       # Usually, you do not have to use this attribute, it instancied by `CurlHTTPRequest` and `CurlMail`.
-       # But in some cases you may want to finalize it to free some small resources.
-       # However, if Curl services are needed once again, this attribute must be manually set.
-       var curl: Curl = new Curl is lazy, writable
-end
-
-# Curl library handle, it is initialized and released with this class
-class Curl
+# Curl library handle
+private class Curl
        super FinalizableOnce
 
-       private var native = new NativeCurl.easy_init
+       var native = new NativeCurl.easy_init
 
-       # Check for correct initialization
+       # Is this instance correctly initialized?
        fun is_ok: Bool do return self.native.is_init
 
        redef fun finalize_once do if is_ok then native.easy_clean
@@ -45,7 +38,7 @@ end
 # CURL Request
 class CurlRequest
 
-       private var curl: Curl = sys.curl
+       private var curl = new Curl
 
        # Shall this request be verbose?
        var verbose: Bool = false is writable
@@ -71,6 +64,14 @@ class CurlRequest
        do
                return new CurlResponseFailed(error_code, error_msg)
        end
+
+       # Close low-level resources associated to this request
+       #
+       # Once closed, this request can't be used again.
+       #
+       # If this service isn't called explicitly, low-level resources
+       # may be freed automatically by the GC.
+       fun close do curl.finalize
 end
 
 # HTTP request builder
@@ -135,7 +136,6 @@ class CurlHTTPRequest
        do
                # Reset libcurl parameters as the lib is shared and options
                # might affect requests from one another.
-               self.curl.native = new NativeCurl.easy_init
                if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
 
                var success_response = new CurlResponseSuccess
@@ -153,8 +153,6 @@ class CurlHTTPRequest
                var st_code = self.curl.native.easy_getinfo_long(new CURLInfoLong.response_code)
                if not st_code == null then success_response.status_code = st_code
 
-               self.curl.native.easy_clean
-
                return success_response
        end
 
@@ -279,6 +277,8 @@ class CurlHTTPRequest
        # Download to file given resource
        fun download_to_file(output_file_name: nullable String): CurlResponse
        do
+               if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
+
                var success_response = new CurlFileResponseSuccess
 
                var callback_receiver: CurlCallbacks = success_response
@@ -447,7 +447,6 @@ class CurlMail
        # Execute Mail request with settings configured through attribute
        fun execute: nullable CurlResponseFailed
        do
-               self.curl.native = new NativeCurl.easy_init
                if not self.curl.is_ok then return answer_failure(0, "Curl instance is not correctly initialized")
 
                var lines = new Array[String]
@@ -521,8 +520,6 @@ class CurlMail
                var err_resp = perform
                if err_resp != null then return err_resp
 
-               self.curl.native.easy_clean
-
                return null
        end
 end
@@ -540,7 +537,10 @@ end
 class CurlResponseFailed
        super CurlResponse
 
+       # Curl error code
        var error_code: Int
+
+       # Curl error message
        var error_msg: String
 
        redef fun to_s do return "{error_msg} ({error_code})"
@@ -568,23 +568,27 @@ end
 class CurlResponseSuccess
        super CurlResponseSuccessIntern
 
-       var body_str = ""
+       # Server HTTP response code
        var status_code = 0
 
-       # Receive body from request due to body callback registering
-       redef fun body_callback(line) do
-               self.body_str = "{self.body_str}{line}"
-       end
+       # Response body as a `String`
+       var body_str = ""
+
+       # Accept part of the response body
+       redef fun body_callback(line) do self.body_str += line
 end
 
 # Success Response Class of a downloaded File
 class CurlFileResponseSuccess
        super CurlResponseSuccessIntern
 
+       # Server HTTP response code
        var status_code = 0
+
        var speed_download = 0.0
        var size_download = 0.0
        var total_time = 0.0
+
        private var file: nullable FileWriter = null
 
        # Receive bytes stream from request due to stream callback registering
@@ -621,7 +625,7 @@ class HeaderMap
        # Get `self` as a single string for HTTP POST
        #
        # Require: `curl.is_ok`
-       fun to_url_encoded(curl: Curl): String
+       private fun to_url_encoded(curl: Curl): String
        do
                assert curl.is_ok
 
diff --git a/lib/curl/extra.nit b/lib/curl/extra.nit
new file mode 100644 (file)
index 0000000..93548d8
--- /dev/null
@@ -0,0 +1,89 @@
+# 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.
+
+# Shortcut services for scripts: `http_get` and `http_download`
+module extra
+
+import curl
+
+redef class Text
+
+       # Execute a simple HTTP GET request to the URL `self`
+       #
+       # Set `accept_status_code` to the expected response HTTP code, defaults to 200.
+       # If a different status code is received, the return code is printed to stderr.
+       #
+       # Returns the response body on success and `null` on error. Prints the error
+       # message to stderr.
+       #
+       # For more control, set HTTP request headers, keep the response status code
+       # and much more, use `CurlHTTPRequest`.
+       #
+       # ~~~nitish
+       # assert "http://example.com/".http_get != null
+       # ~~~
+       fun http_get(accept_status_code: nullable Int): nullable String
+       do
+               var req = new CurlHTTPRequest(self.to_s)
+               var resp = req.execute
+               req.close
+
+               if resp isa CurlResponseSuccess then
+                       if resp.status_code == (accept_status_code or else 200) then
+                               return resp.body_str
+                       else
+                               print_error "HTTP request failed: server returned {resp.status_code}"
+                       end
+               else if resp isa CurlResponseFailed then
+                       print_error "HTTP request failed: {resp.error_msg}"
+               else abort
+               return null
+       end
+
+       # Download the file at URL `self` to `output_path` with a simple HTTP request
+       #
+       # If not set, `output_path` defaults to `self.basename`.
+       #
+       # Set `accept_status_code` to the expected response HTTP code, defaults to 200.
+       # If a different status code is received, the return code is printed to stderr.
+       #
+       # Returns the path to the downloaded file on success and `null` on errors.
+       # Prints the error message to stderr.
+       #
+       # For more control, set HTTP request headers, keep the response status code
+       # and much more, use `CurlHTTPRequest`.
+       #
+       # ~~~nitish
+       # assert "http://example.com/".http_download("index.html") == "example.com"
+       # ~~~
+       fun http_download(output_path: nullable Text, accept_status_code: nullable Int): nullable String
+       do
+               var path = (output_path or else self.basename).to_s
+
+               var req = new CurlHTTPRequest(self.to_s)
+               var resp = req.download_to_file(path)
+               req.close
+
+               if resp isa CurlFileResponseSuccess then
+                       if resp.status_code == (accept_status_code or else 200) then
+                               return path
+                       else
+                               print_error "HTTP request failed: server returned {resp.status_code}"
+                       end
+               else if resp isa CurlResponseFailed then
+                       print_error "HTTP request failed: {resp.error_msg}"
+               else abort
+               return null
+       end
+end
index b2ec70d..4ffa501 100644 (file)
@@ -29,7 +29,7 @@ class LoaderConfig
        redef var default_config_file = "loader.ini"
 
        # Default database host string for MongoDb
-       var default_db_host = "mongodb://localhost:27017/"
+       var default_db_host = "mongodb://mongo:27017/"
 
        # Default database hostname
        var default_db_name = "github_loader"
diff --git a/lib/java/NitObject.java b/lib/java/NitObject.java
new file mode 100644 (file)
index 0000000..a09a9aa
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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.
+ */
+
+package nit.app;
+
+// General class for all references to Nit objects from Java in the FFI
+public class NitObject {
+
+       // Address to the object in Nit memory
+       private long pointer;
+
+       protected NitObject(long pointer) {
+               this.pointer = pointer;
+       }
+}
index 5fafd18..c6a7e2d 100644 (file)
@@ -21,6 +21,7 @@ module ffi_support is
        cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
        ldflags "-L $(JNI_LIB_PATH) -ljvm"
        new_annotation extra_java_files
+       extra_java_files "nit.app.NitObject"
 end
 
 import jvm
index 98b271d..020e0ce 100644 (file)
@@ -22,7 +22,7 @@
 #
 # ~~~
 # # Opens the connexion with the Mongo server.
-# var client = new MongoClient("mongodb://localhost:27017/")
+# var client = new MongoClient("mongodb://mongo:27017/")
 #
 # # Select the database.
 # var db_suffix = "NIT_TESTING_ID".environ
@@ -197,7 +197,7 @@ end
 # Usage:
 #
 # ~~~
-# var uri = "mongodb://localhost:27017/"
+# var uri = "mongodb://mongo:27017/"
 # var client = new MongoClient(uri)
 # assert client.server_uri == uri
 # ~~~
@@ -216,7 +216,7 @@ class MongoClient
        # Returns `null` if an error occured. See `last_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # assert client.server_status["process"] == "mongod"
        # ~~~
        fun server_status: nullable JsonObject do
@@ -230,7 +230,7 @@ class MongoClient
        # Lists available database names.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -259,7 +259,7 @@ class MongoClient
        # There is no need to create a database manually.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -321,7 +321,7 @@ class MongoDb
        # Returns `null` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -346,7 +346,7 @@ class MongoDb
        # Loads or creates a collection by its `name`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -360,7 +360,7 @@ class MongoDb
        # Checks if a collection named `name` exists.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -420,7 +420,7 @@ class MongoCollection
        # Returns `false` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -455,7 +455,7 @@ class MongoCollection
        # Returns `false` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -488,7 +488,7 @@ class MongoCollection
        # Returns `false` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -513,7 +513,7 @@ class MongoCollection
        # No upsert is done, see `save` instead.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -544,7 +544,7 @@ class MongoCollection
        # Returns `-1` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -566,7 +566,7 @@ class MongoCollection
        # Returns `null` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -599,7 +599,7 @@ class MongoCollection
        # * `limit` number of documents to return
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -625,7 +625,7 @@ class MongoCollection
        # Applies an aggregation `pipeline` over the collection.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
@@ -665,7 +665,7 @@ class MongoCollection
        # Returns `null` if an error occured. See `Sys::last_mongoc_error`.
        #
        # ~~~
-       # var client = new MongoClient("mongodb://localhost:27017/")
+       # var client = new MongoClient("mongodb://mongo:27017/")
        # var db_suffix = "NIT_TESTING_ID".environ
        # var db_name = "test_{db_suffix}"
        # var db = client.database(db_name)
index 665ecdb..8c10f6e 100644 (file)
 
 # Neo4j connector through its JSON REST API using curl.
 #
-# For ease of use and testing this module provide a wrapper to the `neo4j` command:
-#
-#     # Start the Neo4j server
-#     var srv = new Neo4jServer
-#     assert srv.start_quiet
-#
 # In order to connect to Neo4j you need a connector:
 #
 #     # Create new Neo4j client
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #     assert client.is_ok
 #
 # The fundamental units that form a graph are nodes and relationships.
@@ -66,39 +60,9 @@ module neo4j
 import curl_json
 import error
 
-# Handles Neo4j server start and stop command
-#
-# `neo4j` binary must be in `PATH` in order to work
-class Neo4jServer
-
-       # Start the local Neo4j server instance
-       fun start: Bool do
-               sys.system("neo4j start console")
-               return true
-       end
-
-       # Like `start` but redirect the console output to `/dev/null`
-       fun start_quiet: Bool do
-               sys.system("neo4j start console > /dev/null")
-               return true
-       end
-
-       # Stop the local Neo4j server instance
-       fun stop: Bool do
-               sys.system("neo4j stop")
-               return true
-       end
-
-       # Like `stop` but redirect the console output to `/dev/null`
-       fun stop_quiet: Bool do
-               sys.system("neo4j stop > /dev/null")
-               return true
-       end
-end
-
 # `Neo4jClient` is needed to communicate through the REST API
 #
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #     assert client.is_ok
 class Neo4jClient
 
@@ -111,8 +75,6 @@ class Neo4jClient
        # REST service to send cypher requests
        private var cypher_url: String
 
-       private var curl = new Curl
-
        init(base_url: String) do
                self.base_url = base_url
                var root = service_root
@@ -142,7 +104,7 @@ class Neo4jClient
 
        # Save the node in base
        #
-       #     var client = new Neo4jClient("http://localhost:7474")
+       #     var client = new Neo4jClient("http://neo4j:7474")
        #
        #     # Create a node
        #     var andres = new NeoNode
@@ -198,7 +160,7 @@ class Neo4jClient
        # Save the edge in base
        # From and to nodes will be created.
        #
-       #     var client = new Neo4jClient("http://localhost:7474")
+       #     var client = new Neo4jClient("http://neo4j:7474")
        #
        #     var andres = new NeoNode
        #     var kate = new NeoNode
@@ -247,7 +209,7 @@ class Neo4jClient
 
        # Retrieve all nodes with specified `lbl`
        #
-       #     var client = new Neo4jClient("http://localhost:7474")
+       #     var client = new Neo4jClient("http://neo4j:7474")
        #
        #     var andres = new NeoNode
        #     andres.labels.add_all(["Human", "Male"])
@@ -273,7 +235,7 @@ class Neo4jClient
 
        # Retrieve nodes belonging to all the specified `labels`.
        #
-       #     var client = new Neo4jClient("http://localhost:7474")
+       #     var client = new Neo4jClient("http://neo4j:7474")
        #
        #     var andres = new NeoNode
        #     andres.labels.add_all(["Human", "Male"])
@@ -389,7 +351,7 @@ end
 #
 # Example:
 #
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #     var query = new CypherQuery
 #     query.nmatch("(n)-[r:LOVES]->(m)")
 #     query.nwhere("n.name=\"Andres\"")
@@ -508,7 +470,7 @@ end
 # Then we can link the entity to the base:
 #
 #     # Init client
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #     client.save_node(andres)
 #     # The node is now linked
 #     assert andres.is_linked
@@ -600,7 +562,7 @@ end
 #
 # Creating new nodes:
 #
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #
 #     var andres = new NeoNode
 #     andres.labels.add "Person"
@@ -713,7 +675,7 @@ end
 #
 # Create a relationship:
 #
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #     # Create nodes
 #     var andres = new NeoNode
 #     andres["name"] = "Andres"
@@ -806,7 +768,7 @@ end
 #
 # Example:
 #
-#     var client = new Neo4jClient("http://localhost:7474")
+#     var client = new Neo4jClient("http://neo4j:7474")
 #
 #     var node1 = new NeoNode
 #     var node2 = new NeoNode
@@ -1013,7 +975,7 @@ end
 # This is a representation of a neo job in JSON Format
 #
 # Each job description should contain a `to` attribute, with a value relative to the data API root
-# (so http://localhost:7474/db/data/node becomes just /node), and a `method` attribute containing
+# (so http://neo4j:7474/db/data/node becomes just /node), and a `method` attribute containing
 # HTTP verb to use.
 #
 # Optionally you may provide a `body` attribute, and an `id` attribute to help you keep track
index bbc9c95..f7c6346 100644 (file)
@@ -823,7 +823,7 @@ class UserForm
        end
 end
 
-var mongo = new MongoClient("mongodb://localhost:27017/")
+var mongo = new MongoClient("mongodb://mongo:27017/")
 var db = mongo.database("mongo_example")
 
 var app = new App
index b7de4fe..00b1421 100644 (file)
@@ -60,7 +60,7 @@ class UserList
        end
 end
 
-var mongo = new MongoClient("mongodb://localhost:27017/")
+var mongo = new MongoClient("mongodb://mongo:27017/")
 var db = mongo.database("mongo_example")
 
 var app = new App
index 2f84be7..3dd71fb 100644 (file)
@@ -197,9 +197,6 @@ class GithubOAuthCallBack
                        return
                end
 
-               # FIXME reinit curl before next request to avoid weird 404
-               curl = new Curl
-
                # Load github user
                var gh_api = new GithubAPI(access_token)
                var user = gh_api.load_auth_user
index 72003ec..c054d5a 100644 (file)
 # # Let's wrap it all together in a Popcorn app:
 #
 # # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
 # var db = mongo.database("tests_app_{100000.rand}")
 # var coll = db.collection("books")
 #
@@ -126,7 +126,7 @@ import mongodb::queries
 redef class AppConfig
 
        # Default database host string for MongoDb
-       var default_db_host = "mongodb://localhost:27017/"
+       var default_db_host = "mongodb://mongo:27017/"
 
        # Default database hostname
        var default_db_name = "popcorn"
@@ -290,7 +290,7 @@ end
 # # The repository can then be used with User instances:
 #
 # # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
 # var db = mongo.database("tests")
 # var coll = db.collection("test_pop_repo_{100000.rand}")
 #
index d8a475e..6c209ef 100644 (file)
@@ -37,7 +37,7 @@
 # animals.add(turtle)
 #
 # var db_suffix = "NIT_TESTING_ID".environ
-# var db = new Postgres.open("dbname=postgres")
+# var db = new Postgres.open("host=postgres user=postgres dbname=postgres")
 #
 # assert db_is_open: not db.is_closed
 # assert create_table: db.create_table("IF NOT EXISTS animals_{db_suffix} (aname TEXT PRIMARY KEY, kind TEXT NOT NULL, age INT NOT NULL)") else print db.error
diff --git a/misc/docker/ci-local/README.md b/misc/docker/ci-local/README.md
new file mode 100644 (file)
index 0000000..4ac947a
--- /dev/null
@@ -0,0 +1,34 @@
+# Local setup of services for tests
+
+Some tests and libs require specific external services like postgres.
+To simplify the automation and the setup, we just assume these services are accessible at specific hostnames.
+
+When running and testing nit within a docker, it is easy to compose/link nit with other dockers/services.
+See also .gitlab-ci.yml
+
+~~~
+$ docker run --link postgres [...]
+~~~
+
+When running and testing on a local host, one can setup those services locally then add aliases for localhost in `/etc/hosts`.
+
+~~~
+$ cat /etc/hosts
+[...]
+127.0.0.1 postgres
+~~~
+
+An other way is to run/test on a local host and have the services running in dockers.
+
+Because accessing a specific container by its name is cumbersome, a proposed way is to:
+
+* run these services with specific and fixed IPs
+* add these IPs to `/etc/hosts`
+
+~~~
+$ cd misc/docker/ci-local
+$ docker-compose up -d
+$ cat /etc/hosts
+[...]
+172.16.238.4 postgres
+~~~
diff --git a/misc/docker/ci-local/docker-compose.yml b/misc/docker/ci-local/docker-compose.yml
new file mode 100644 (file)
index 0000000..aabc9f7
--- /dev/null
@@ -0,0 +1,29 @@
+version: '2'
+services:
+        mongo:
+                image: mongo
+                restart: always
+                networks:
+                        nitci:
+                                ipv4_address: 172.16.238.2
+        neo4j:
+                image: neo4j:2.3
+                restart: always
+                environment:
+                        NEO4J_AUTH: none
+                networks:
+                        nitci:
+                                ipv4_address: 172.16.238.3
+        postgres:
+                image: postgres
+                restart: always
+                networks:
+                        nitci:
+                                ipv4_address: 172.16.238.4
+networks:
+        nitci:
+                driver: bridge
+                ipam:
+                        driver: default
+                        config:
+                                - subnet: 172.16.238.0/24
index 0df6ea2..000c0c1 100644 (file)
@@ -47,7 +47,8 @@ RUN dpkg --add-architecture i386 \
                libsqlite3-dev \
                libx11-dev \
                libxdg-basedir-dev \
-               postgresql \
+               netcat \
+               psmisc \
                # Packages needed for contrib, platforms and FFI
                ant \
                clang \
@@ -56,6 +57,7 @@ RUN dpkg --add-architecture i386 \
                file \
                gnupg \
                gnuplot-nox \
+               imagemagick \
                inkscape \
                libopenmpi-dev \
                time \
@@ -71,11 +73,37 @@ RUN dpkg --add-architecture i386 \
        && apt-get install -y nodejs \
        && rm -rf /var/lib/apt/lists/*
 
+# Install OpenGL validator
+RUN git clone https://github.com/KhronosGroup/glslang.git --depth=1 \
+       && mkdir -p glslang/build \
+       && cd glslang/build \
+       && cmake .. \
+       && make \
+       && make install
+
+# Install android sdk/ndk
+RUN mkdir -p /opt \
+       && cd /opt \
+       # Android SDK
+       && curl https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip -o android-sdk-linux.zip \
+       && unzip -q android-sdk-linux.zip -d android-sdk-linux \
+       # Download a specific ndk version because old versions are not available trough sdkmanager
+       && curl https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip -o android-ndk-linux.zip \
+       && unzip -q android-ndk-linux.zip \
+       && mv android-ndk-r17c android-sdk-linux/ndk-bundle \
+       && rm android-sdk-linux.zip android-ndk-linux.zip
+RUN cd /opt \
+       && yes | android-sdk-linux/tools/bin/sdkmanager "build-tools;27.0.0" "cmake;3.6.4111459" platform-tools tools  --verbose\
+       && yes | android-sdk-linux/tools/bin/sdkmanager --licenses --verbose
+# TODO: predownload bwdgc and gradle?
+
 # Setup environment variables
+ENV ANDROID_HOME=/opt/android-sdk-linux/
 ENV JAVA_HOME=/usr/lib/jvm/default-java/
 ENV JNI_LIB_PATH=$JAVA_HOME/jre/lib/amd64/server/
 ENV LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server/
 
+# Used by CI to render junit files to html
 RUN pip3 install junit2html
 
 #  Prepare to install npm (npm is not packaged for debian:stretch)
index 9a5c27f..41a1342 100755 (executable)
@@ -49,7 +49,7 @@ for ref in `git rev-list --no-merges "$from".."$to"`; do
        err=1
 done
 
-rm check_signedoff_list.out 2> /dev/null
+rm check_signedoff_list.out 2> /dev/null || true
 
 if test "$err" = 1; then
        echo ""
index 7ea0c27..334a54b 100755 (executable)
@@ -25,4 +25,4 @@ git clone --depth=1 -b android https://github.com/xymus/bdwgc.git || exit 1
 # Setup libatomic_ops too
 cd bdwgc || exit 1
 git submodule init || exit 1
-git submodule update --depth=1 || exit 1
+git submodule update || exit 1
index 9a03c5a..61c1f9c 100644 (file)
@@ -105,7 +105,8 @@ end
 
 # An extern file to compile
 class ExternFile
-       # The filename of the file
+
+       # Filename relative to the nit-compile folder
        var filename: String
 
        # The name of the target in the Makefile
@@ -118,6 +119,7 @@ class ExternFile
 
        fun compiles_to_o_file: Bool do return false
 
+       # Is `self` a Java file to include in the JAR archive?
        fun add_to_jar: Bool do return false
 
        # Additional libraries needed for the compilation
index 32dd807..e786374 100644 (file)
@@ -465,6 +465,13 @@ ifneq ($(findstring MINGW64,$(uname_S)),)
        CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast
 endif
 
+# Add the compilation dir to the Java CLASSPATH
+ifeq ($(CLASSPATH),)
+       CLASSPATH := .
+else
+       CLASSPATH := $(CLASSPATH):.
+endif
+
 """
 
                makefile.write("all: {outpath}\n")
@@ -528,8 +535,7 @@ endif
                var java_files = new Array[ExternFile]
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
-                       var ff = f.filename.basename
-                       makefile.write("{o}: {ff}\n")
+                       makefile.write("{o}: {f.filename}\n")
                        makefile.write("\t{f.makefile_rule_content}\n\n")
                        dep_rules.add(f.makefile_rule_name)
 
@@ -712,7 +718,7 @@ abstract class AbstractCompiler
                stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
                stream.close
 
-               extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
+               extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
        end
 
        # Compile C headers
index 1f59771..ce0f71b 100644 (file)
@@ -29,7 +29,7 @@ redef class MModule
                return v.compiler.modelbuilder.mmodule2node(self)
        end
 
-       redef fun finalize_ffi(compiler: AbstractCompiler)
+       redef fun finalize_ffi(compiler)
        do
                if not uses_ffi then return
 
@@ -53,7 +53,7 @@ extern void nitni_global_ref_decr(void*);
                nitni_ccu.write_as_nitni(self, v.compiler.toolchain.compile_dir)
 
                for file in nitni_ccu.files do
-                       var f = new ExternCFile(file, cflags)
+                       var f = new ExternCFile(file.basename, cflags)
                        f.pkgconfigs.add_all pkgconfigs
                        v.compiler.extern_bodies.add(f)
                end
index 13a6e92..f660114 100644 (file)
@@ -171,7 +171,7 @@ class CPPCompilationUnit
 
                files.add("{compdir}/{c_file}")
 
-               return new ExternCppFile("{compdir}/{c_file}", mmodule)
+               return new ExternCppFile(c_file, mmodule)
        end
 end
 
@@ -180,8 +180,8 @@ class ExternCppFile
 
        var mmodule: MModule
 
-       redef fun makefile_rule_name do return "{filename.basename}.o"
-       redef fun makefile_rule_content do return "$(CXX) $(CFLAGS) {mmodule.cppflags[""].join(" ")} -c {filename.basename} -o {filename.basename}.o"
+       redef fun makefile_rule_name do return "{filename}.o"
+       redef fun makefile_rule_content do return "$(CXX) $(CFLAGS) {mmodule.cppflags[""].join(" ")} -c {filename} -o {filename}.o"
        redef fun compiles_to_o_file do return true
 end
 
index e6dfca2..c355441 100644 (file)
@@ -29,7 +29,7 @@ end
 
 redef class MModule
        # Extra Java files to compile with the module
-       private var extra_java_files: nullable Array[JavaFile] = null
+       private var extra_java_files: nullable Array[ExtraJavaFile] = null
 end
 
 private class JavaExtraFilesPhase
@@ -60,18 +60,20 @@ private class JavaExtraFilesPhase
                var mmodule = nmodule.mmodule.as(not null)
                var java_files = mmodule.extra_java_files
                if java_files == null then
-                       java_files = new Array[JavaFile]
+                       java_files = new Array[ExtraJavaFile]
                        mmodule.extra_java_files = java_files
                end
 
                var format_error = "Syntax Error: `{annot_name}` expects its arguments to be paths to java files."
                for arg in args do
-                       var path = arg.as_string
-                       if path == null then
+                       var name = arg.as_string
+                       if name == null or name.is_empty then
                                modelbuilder.error(arg, format_error)
                                return
                        end
 
+                       var path = name.split(".").last + ".java"
+
                        # 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}"
@@ -81,7 +83,7 @@ private class JavaExtraFilesPhase
                                continue
                        end
 
-                       var file = new JavaFile(path)
+                       var file = new ExtraJavaFile(name, path)
                        mmodule.ffi_files.add file
                        java_files.add file
                end
@@ -93,11 +95,28 @@ redef class JavaLanguage
        do
                super
 
-               # also copy over the java files
+               # Also copy over the extra Java files
                var extra_java_files = mmodule.extra_java_files
                if extra_java_files != null then for file in extra_java_files do
-                       var path = file.filename
-                       path.file_copy_to("{compdir}/{path.basename}")
+
+                       var dir = compdir / file.filename.dirname
+                       dir.mkdir
+
+                       file.src_path.file_copy_to(compdir/file.filename)
                end
        end
 end
+
+# User supplied Java file to include with the app for use from the FFI
+class ExtraJavaFile
+       super JavaFile
+
+       autoinit full_name, src_path
+
+       redef var full_name
+
+       # Path to the original user file
+       var src_path: String
+
+       redef fun filename do return full_name.replace(".", "/") + ".java"
+end
index 0dd65be..fe8bc19 100644 (file)
@@ -105,7 +105,7 @@ class JavaLanguage
                        jni_signature_alt = mclass_type.jni_signature_alt
                        return_type = mclass_type
                else
-                       params.add "self"
+                       params.add to_java_call_context.cast_to(mclass_type, "self")
                        if signature.return_mtype != null then
                                var ret_mtype = signature.return_mtype
                                ret_mtype = ret_mtype.resolve_for(mclass_type, mclass_type, mmodule, true)
@@ -165,6 +165,9 @@ class JavaLanguage
 
        redef fun compile_to_files(mmodule, compdir)
        do
+               var ffi_ccu = ffi_ccu
+               assert ffi_ccu != null
+
                # Make sure we have a .java file
                mmodule.ensure_java_files
 
@@ -172,7 +175,65 @@ class JavaLanguage
                mmodule.insert_compiler_options
 
                # Enable linking C callbacks to java native methods
-               mmodule.ensure_linking_callback_methods(ffi_ccu.as(not null))
+               mmodule.ensure_linking_callback_methods(ffi_ccu)
+
+               # Function to build instances to the Java class NitObject
+               var callbacks = mmodule.callbacks_used_from_java.callbacks
+               if callbacks.not_empty then
+                       var cf = new CFunction("jobject nit_ffi_with_java_new_nit_object(JNIEnv *env, void *data)")
+                       cf.exprs.add """
+       // retrieve the current JVM
+       Sys sys = Pointer_sys(NULL);
+
+       jclass java_class = Sys_load_jclass(sys, "nit/app/NitObject");
+       if (java_class == NULL) {
+               PRINT_ERROR("Nit FFI with Java error: failed to load class NitObject.\\n");
+               (*env)->ExceptionDescribe(env);
+               exit(1);
+       }
+
+       jmethodID java_init = (*env)->GetMethodID(env, java_class, "<init>", "(J)V");
+       if (java_init == NULL) {
+               PRINT_ERROR("Nit FFI with Java error: NitObject constructor not found.\\n");
+               (*env)->ExceptionDescribe(env);
+               exit(1);
+       }
+
+       jobject nit_object = (*env)->NewObject(env, java_class, java_init, (jlong)data);
+       if (nit_object == NULL) {
+               PRINT_ERROR("Nit FFI with Java error: NitObject construction failed.\\n");
+               (*env)->ExceptionDescribe(env);
+               exit(1);
+       }
+
+       return nit_object;
+       """
+                       ffi_ccu.add_local_function cf
+
+                       # Function to extract the pointer held by instances of the Java class NitObject
+                       cf = new CFunction("void *nit_ffi_with_java_nit_object_data(JNIEnv *env, jobject nit_object)")
+                       cf.exprs.add """
+       Sys sys = Pointer_sys(NULL);
+       jclass java_class = Sys_load_jclass(sys, "nit/app/NitObject");
+       if (java_class == NULL) {
+               PRINT_ERROR("Nit FFI with Java error: failed to load class NitObject.\\n");
+               (*env)->ExceptionDescribe(env);
+               exit(1);
+       }
+
+       jfieldID java_field = (*env)->GetFieldID(env, java_class, "pointer", "J");
+       if (java_field == NULL) {
+               PRINT_ERROR("Nit FFI with Java error: NitObject field not found.\\n");
+               (*env)->ExceptionDescribe(env);
+               exit(1);
+       }
+
+       jlong data = (*env)->GetLongField(env, nit_object, java_field);
+
+       return (void*)data;
+       """
+                       ffi_ccu.add_local_function cf
+               end
 
                # Java implementation code
                var java_file = mmodule.java_file
@@ -343,7 +404,7 @@ class JavaClassTemplate
        fun write_to_files(compdir: String): ExternFile
        do
                var filename = "{java_class_name}.java"
-               var filepath = "{compdir}/{filename}"
+               var filepath = compdir/filename
 
                write_to_file filepath
 
@@ -364,8 +425,11 @@ end
 class JavaFile
        super ExternFile
 
-       redef fun makefile_rule_name do return "{filename.basename(".java")}.class"
-       redef fun makefile_rule_content do return "javac {filename.basename} -d ."
+       # Full Java class name: package and class
+       fun full_name: String do return filename.basename(".java")
+
+       redef fun makefile_rule_name do return full_name.replace(".", "/") + ".class"
+       redef fun makefile_rule_content do return "javac {filename} -d ."
        redef fun add_to_jar do return true
 end
 
@@ -380,8 +444,24 @@ end
 private class ToJavaCallContext
        super CallContext
 
-       redef fun cast_to(mtype, name) do return "({mtype.jni_type})({name})"
-       redef fun cast_from(mtype, name) do return "({mtype.cname})({name})"
+       redef fun cast_to(mtype, name)
+       do
+               if mtype.java_is_nit_object then
+                       return "nit_ffi_with_java_new_nit_object(nit_ffi_jni_env, {name})"
+               else
+                       return "({mtype.jni_type})({name})"
+               end
+       end
+
+       redef fun cast_from(mtype, name)
+       do
+               if mtype.java_is_nit_object then
+                       return "({mtype.cname})nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, {name})"
+               else
+                       return "({mtype.cname})({name})"
+               end
+       end
+
        redef fun name_mtype(mtype) do return mtype.jni_type
 end
 
@@ -389,8 +469,24 @@ end
 private class FromJavaCallContext
        super CallContext
 
-       redef fun cast_to(mtype, name) do return "({mtype.cname})({name})"
-       redef fun cast_from(mtype, name) do return "({mtype.jni_type})({name})"
+       redef fun cast_to(mtype, name)
+       do
+               if mtype.java_is_nit_object then
+                       return "({mtype.cname})nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, {name})"
+               else
+                       return "({mtype.cname})({name})"
+               end
+       end
+
+       redef fun cast_from(mtype, name)
+       do
+               if mtype.java_is_nit_object then
+                       return "nit_ffi_with_java_new_nit_object(nit_ffi_jni_env, {name})"
+               else
+                       return "({mtype.jni_type})({name})"
+               end
+       end
+
        redef fun name_mtype(mtype) do return mtype.jni_type
 end
 
@@ -452,21 +548,23 @@ redef class MType
        #
        # * Primitives common to both languages use their Java primitive type
        # * Nit extern Java classes are represented by their full Java type
-       # * Other Nit objects are represented by `int` in Java. It holds the
-       #       pointer to the underlying C structure.
-       #       TODO create static Java types to store and hide the pointer
-       private fun java_type: String do return "int"
+       # * Other Nit objects are represented by `NitObject` in Java, a class
+       #   encapsulating the pointer to the underlying C structure.
+       private fun java_type: String do return "nit.app.NitObject"
+
+       # Is this type opaque in Java? As so it is represented by `nit.app.NitObject`.
+       private fun java_is_nit_object: Bool do return true
 
        # JNI type name (in C)
        #
        # So this is a C type, usually defined in `jni.h`
-       private fun jni_type: String do return "long"
+       private fun jni_type: String do return "jobject"
 
        # JNI short type name (for signatures)
        #
        # Is used by `MMethod::build_jni_format` to pass a Java method signature
        # to the JNI function `GetStaticMetodId`.
-       private fun jni_format: String do return "I"
+       private fun jni_format: String do return "Lnit/app/NitObject;"
 
        # Type name appearing within JNI function names.
        #
@@ -476,20 +574,22 @@ redef class MType
 
        redef fun compile_callback_to_java(mmodule, mainmodule, ccu)
        do
+               if self isa MClassType and mclass.ftype isa ForeignJavaType then return
+
                var java_file = mmodule.java_file
-               if java_file == null then return
+               if java_file == null or mmodule.callbacks_used_from_java.callbacks.is_empty then return
 
                for variation in ["incr", "decr"] do
                        var friendly_name = "{mangled_cname}_{variation}_ref"
 
                        # C
-                       var csignature = "void {mmodule.impl_java_class_name}_{friendly_name}(JNIEnv *env, jclass clazz, jint object)"
+                       var csignature = "void {mmodule.impl_java_class_name}_{friendly_name}(JNIEnv *nit_ffi_jni_env, jclass clazz, jobject object)"
                        var cf = new CFunction("JNIEXPORT {csignature}")
-                       cf.exprs.add "\tnitni_global_ref_{variation}((void*)(long)object);"
+                       cf.exprs.add "\tnitni_global_ref_{variation}(nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, object));"
                        ccu.add_non_static_local_function cf
 
                        # Java
-                       java_file.class_content.add "private native static void {friendly_name}(int object);\n"
+                       java_file.class_content.add "private native static void {friendly_name}(nit.app.NitObject object);\n"
                end
        end
 
@@ -498,7 +598,7 @@ redef class MType
                var arr = new Array[String]
                for variation in ["incr", "decr"] do
                        var friendly_name = "{mangled_cname}_{variation}_ref"
-                       var jni_format = "(I)V"
+                       var jni_format = "(Lnit/app/NitObject;)V"
                        var cname = "{from_mmodule.impl_java_class_name}_{friendly_name}"
                        arr.add """{"{{{friendly_name}}}", "{{{jni_format}}}", {{{cname}}}}"""
                end
@@ -526,6 +626,16 @@ redef class MClassType
                return super
        end
 
+       redef fun java_is_nit_object
+       do
+               var ftype = mclass.ftype
+               if ftype isa ForeignJavaType then return false
+
+               var java_primitives = once new HashSet[String].from(
+                       ["Bool", "Char", "Int", "Float", "Byte", "Int8", "Int16", "UInt16", "Int32", "UInt32"])
+               return not java_primitives.has(mclass.name)
+       end
+
        redef fun jni_type
        do
                var ftype = mclass.ftype
@@ -625,6 +735,22 @@ redef class MClassType
                if mclass.name == "UInt32" then return "Int"
                return super
        end
+
+       redef fun compile_callback_to_java(mmodule, mainmodule, ccu)
+       do
+               # Don't generate functions for reference counters on extern classes
+               if mclass.ftype != null then return
+
+               super
+       end
+
+       redef fun jni_methods_declaration(from_mmodule)
+       do
+               # Don't generate functions for reference counters on extern classes
+               if mclass.ftype != null then return new Array[String]
+
+               return super
+       end
 end
 
 redef class MMethod
@@ -690,8 +816,7 @@ redef class MMethod
 
                var cparams = new List[String]
 
-               # This is different
-               cparams.add "JNIEnv *env"
+               cparams.add "JNIEnv *nit_ffi_jni_env"
                cparams.add "jclass clazz"
 
                if not self.is_init then
index 2245a92..b8d572b 100644 (file)
@@ -57,7 +57,7 @@ redef class MModule
 
                ffi_ccu.write_as_impl(self, compdir)
                for filename in ffi_ccu.files do
-                       var f = new ExternCFile(filename, cflags)
+                       var f = new ExternCFile(filename.basename, cflags)
                        f.pkgconfigs.add_all pkgconfigs
                        ffi_files.add(f)
                end
index 9c0545a..24ce15d 100644 (file)
@@ -156,7 +156,7 @@ private class ObjCCompilationUnit
 
                mmodule.ldflags.add_one("", "-lobjc")
 
-               return new ExternObjCFile(compdir/c_file, mmodule)
+               return new ExternObjCFile(c_file, mmodule)
        end
 end
 
@@ -169,7 +169,7 @@ class ExternObjCFile
 
        redef fun makefile_rule_name do return "{filename.basename(".m")}_m.o"
        redef fun makefile_rule_content do
-               return "clang $(CFLAGS) -c {filename.basename} -o {makefile_rule_name}"
+               return "clang $(CFLAGS) -c {filename} -o {makefile_rule_name}"
        end
        redef fun compiles_to_o_file do return true
 end
index 44f6854..acd783a 100644 (file)
@@ -404,7 +404,7 @@ redef class ExternCFile
                var cflags = mmodule.cflags[""].join(" ") + " " + pkg_cflags
                var obj = compile_dir / filename.basename(".c") + ".o"
 
-               var cmd = "{v.c_compiler} -Wall -c -fPIC -I {compile_dir} -g -o {obj} {filename} {cflags}"
+               var cmd = "{v.c_compiler} -Wall -c -fPIC -I {compile_dir} -g -o {obj} {compile_dir / filename} {cflags}"
                if sys.system(cmd) != 0 then
                         v.fatal "FFI Error: Failed to compile C code using `{cmd}`"
                         return false
index 3c85633..9bff506 100644 (file)
@@ -419,8 +419,10 @@ target_link_libraries(nit_app gc-lib
                for mmodule in compiler.mainmodule.in_importation.greaters do
                        var extra_java_files = mmodule.extra_java_files
                        if extra_java_files != null then for file in extra_java_files do
-                               var path = file.filename
-                               path.file_copy_to(java_dir/path.basename)
+                               var path = file.src_path
+                               var dir = file.filename.dirname
+                               (java_dir/dir).mkdir
+                               path.file_copy_to(java_dir/file.filename)
                        end
                end
 
index c611822..669601e 100644 (file)
@@ -1,13 +1,5 @@
-android
 emscripten
-java
 glsl
-mongo
 mpi
-neo
 objc
-postgres
-action_nitro
-asteronits
-nitrpg
 wiringPi
index 757c963..0d2b4d1 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import curl
+intrude import curl
 
 class CallbackManager
   super CurlCallbacks
 
-  redef fun body_callback(line: String) do end
+  redef fun body_callback(line) do end
 end
 
-fun error_manager(err: CURLCode) do if not err.is_ok then print err
+private fun error_manager(err: CURLCode) do if not err.is_ok then print err
 
 var url = "http://example.org/"
 
@@ -210,4 +210,4 @@ var hashMapRefined = new HeaderMap
 hashMapRefined["hello"] = "toto"
 hashMapRefined["hello"] = "tata"
 hashMapRefined["allo"] = "foo"
-print hashMapRefined.to_url_encoded(sys.curl)
+print hashMapRefined.to_url_encoded(new Curl)
index c57135a..aa87613 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 module test_ffi_java_annot_files is
-       extra_java_files("test_ffi_java_annot_files_a.java")
+       extra_java_files("test_ffi_java_annot_files_a")
 end
 
 import java
index a1ee088..46fe939 100644 (file)
@@ -1 +1 @@
-localhost 7474 test_prog/test_prog.nit
+neo4j 7474 test_prog/test_prog.nit
index 974a80a..d240047 100644 (file)
@@ -17,12 +17,9 @@ import neo4j
 # key used to loosely assume unicity and prevent conflicting db accesses
 var key = "NIT_TESTING_ID".environ.to_i
 
-var srv = new Neo4jServer
-srv.start_quiet
-
 print "# Test local\n"
 
-var client = new Neo4jClient("http://localhost:7474")
+var client = new Neo4jClient("http://neo4j:7474")
 assert client.is_ok
 
 # Clear the previous objects, if any
@@ -91,7 +88,7 @@ print "{kate["name"].to_s} IS LOVED BY {kate.in_nodes("LOVES").first["name"].to_
 
 print "\n# Test lazy\n"
 
-client = new Neo4jClient("http://localhost:7474/")
+client = new Neo4jClient("http://neo4j:7474/")
 assert client.is_ok
 
 # Read Andres
index 464eb2f..499f7ef 100644 (file)
@@ -14,9 +14,6 @@
 
 import neo4j
 
-var srv = new Neo4jServer
-srv.start_quiet
-
 # key used to loosely assume unicity and prevent conflicting db accesses
 var key = "NIT_TESTING_ID".environ.to_i
 
@@ -37,7 +34,7 @@ kate["status"] = false
 var loves = new NeoEdge(andres, "LOVES", kate)
 loves["since"] = 1999
 
-var client = new Neo4jClient("http://localhost:7474")
+var client = new Neo4jClient("http://neo4j:7474")
 assert client.is_ok
 
 # Clear the previous objects, if any
@@ -64,7 +61,7 @@ var andres_url = andres.url.to_s
 var kate_url = kate.url.to_s
 var loves_url = loves.url.to_s
 
-client = new Neo4jClient("http://localhost:7474")
+client = new Neo4jClient("http://neo4j:7474")
 assert client.is_ok
 
 # Read Andres
index b7c5a49..d718469 100644 (file)
@@ -19,7 +19,7 @@ module test_postgres_native
 import postgresql::native_postgres
 
 var db_suffix = "NIT_TESTING_ID".environ
-var db = new NativePostgres.connectdb("dbname=postgres")
+var db = new NativePostgres.connectdb("host=postgres user=postgres dbname=postgres")
 assert postgres_open: db.status.is_ok else print_error db.error
 
 var result = db.exec("CREATE TABLE IF NOT EXISTS animals_{db_suffix} (aname TEXT PRIMARY KEY, class TEXT NOT NULL, sex INTEGER)")
index 4421a02..6714a04 100644 (file)
@@ -19,7 +19,7 @@ module test_postgres_nity
 import postgresql::postgres
 
 var db_suffix = "NIT_TESTING_ID".environ
-var db = new Postgres.open("dbname=postgres")
+var db = new Postgres.open("host=postgres user=postgres dbname=postgres")
 assert open_db: not db.is_closed else print db.error
 
 assert create_table: db.create_table("IF NOT EXISTS users_{db_suffix} (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else