From: Jean Privat Date: Fri, 1 Mar 2019 00:08:44 +0000 (-0500) Subject: Merge: Java FFI: use class NitObject for references to Nit objects from Java X-Git-Url: http://nitlanguage.org?hp=8989d627b35f762f8332a1ede1dde090213d200a Merge: Java FFI: use class NitObject for references to Nit objects from Java 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 --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a70ece..7bb7ada 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 ######################################################## diff --git a/benchmarks/bench_engines.sh b/benchmarks/bench_engines.sh index dfe66ae..ad25612 100755 --- a/benchmarks/bench_engines.sh +++ b/benchmarks/bench_engines.sh @@ -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 diff --git a/contrib/benitlux/src/client/android.nit b/contrib/benitlux/src/client/android.nit index ee51327..3495f6c 100644 --- a/contrib/benitlux/src/client/android.nit +++ b/contrib/benitlux/src/client/android.nit @@ -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() { diff --git a/contrib/nitrpg/src/game.nit b/contrib/nitrpg/src/game.nit index b1d8916..5413f43 100644 --- a/contrib/nitrpg/src/game.nit +++ b/contrib/nitrpg/src/game.nit @@ -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 diff --git a/contrib/nitrpg/src/test_helper.nit b/contrib/nitrpg/src/test_helper.nit index 919ccfc..9d71ca3 100644 --- a/contrib/nitrpg/src/test_helper.nit +++ b/contrib/nitrpg/src/test_helper.nit @@ -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) diff --git a/contrib/nitrpg/src/web.nit b/contrib/nitrpg/src/web.nit index c563af7..3d54672 100644 --- a/contrib/nitrpg/src/web.nit +++ b/contrib/nitrpg/src/web.nit @@ -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) diff --git a/lib/android/NitActivity.java b/lib/android/NitActivity.java index 2f712f1..ca15d38 100644 --- a/lib/android/NitActivity.java +++ b/lib/android/NitActivity.java @@ -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 diff --git a/lib/android/README.md b/lib/android/README.md index ffd961c..c948c48 100644 --- a/lib/android/README.md +++ b/lib/android/README.md @@ -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 """""" ~~~ @@ -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 diff --git a/lib/android/activities.nit b/lib/android/activities.nit index a457899..76e3f07 100644 --- a/lib/android/activities.nit +++ b/lib/android/activities.nit @@ -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() { diff --git a/lib/android/assets_and_resources.nit b/lib/android/assets_and_resources.nit index 930bc17..2ca56d4 100644 --- a/lib/android/assets_and_resources.nit +++ b/lib/android/assets_and_resources.nit @@ -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); diff --git a/lib/android/bundle/bundle.nit b/lib/android/bundle/bundle.nit index e02702a..521355f 100644 --- a/lib/android/bundle/bundle.nit +++ b/lib/android/bundle/bundle.nit @@ -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 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 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 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 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; diff --git a/lib/android/intent/intent_api10.nit b/lib/android/intent/intent_api10.nit index f9e6c3b..1c05f89 100644 --- a/lib/android/intent/intent_api10.nit +++ b/lib/android/intent/intent_api10.nit @@ -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 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 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 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); diff --git a/lib/android/nit_activity.nit b/lib/android/nit_activity.nit index f084958..980270c 100644 --- a/lib/android/nit_activity.nit +++ b/lib/android/nit_activity.nit @@ -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); } diff --git a/lib/android/service/NitService.java b/lib/android/service/NitService.java index b0767d6..fd7e79f 100644 --- a/lib/android/service/NitService.java +++ b/lib/android/service/NitService.java @@ -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); } diff --git a/lib/android/service/at_boot.nit b/lib/android/service/at_boot.nit index a2c1868..9669fe7 100644 --- a/lib/android/service/at_boot.nit +++ b/lib/android/service/at_boot.nit @@ -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 """ """ diff --git a/lib/android/service/service.nit b/lib/android/service/service.nit index 901a0e2..823d16e 100644 --- a/lib/android/service/service.nit +++ b/lib/android/service/service.nit @@ -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 """""" 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); diff --git a/lib/android/shared_preferences/shared_preferences_api10.nit b/lib/android/shared_preferences/shared_preferences_api10.nit index 0464b67..f012ed2 100644 --- a/lib/android/shared_preferences/shared_preferences_api10.nit +++ b/lib/android/shared_preferences/shared_preferences_api10.nit @@ -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 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) { diff --git a/lib/android/shared_preferences/shared_preferences_api11.nit b/lib/android/shared_preferences/shared_preferences_api11.nit index 85623d8..9d2dbcf 100644 --- a/lib/android/shared_preferences/shared_preferences_api11.nit +++ b/lib/android/shared_preferences/shared_preferences_api11.nit @@ -32,7 +32,7 @@ redef extern class NativeSharedPreferences HashSet[JavaString].add in "Java" `{ Set def_value = new HashSet(); Set 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 java_set = new HashSet(); - 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)); diff --git a/lib/android/ui/ui.nit b/lib/android/ui/ui.nit index 60f7909..767039b 100644 --- a/lib/android/ui/ui.nit +++ b/lib/android/ui/ui.nit @@ -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(){ diff --git a/lib/curl/curl.nit b/lib/curl/curl.nit index 34b2576..18f05c8 100644 --- a/lib/curl/curl.nit +++ b/lib/curl/curl.nit @@ -16,27 +16,20 @@ # 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 index 0000000..93548d8 --- /dev/null +++ b/lib/curl/extra.nit @@ -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 diff --git a/lib/github/loader.nit b/lib/github/loader.nit index b2ec70d..4ffa501 100644 --- a/lib/github/loader.nit +++ b/lib/github/loader.nit @@ -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 index 0000000..a09a9aa --- /dev/null +++ b/lib/java/NitObject.java @@ -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; + } +} diff --git a/lib/java/ffi_support.nit b/lib/java/ffi_support.nit index 5fafd18..c6a7e2d 100644 --- a/lib/java/ffi_support.nit +++ b/lib/java/ffi_support.nit @@ -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 diff --git a/lib/mongodb/mongodb.nit b/lib/mongodb/mongodb.nit index 98b271d..020e0ce 100644 --- a/lib/mongodb/mongodb.nit +++ b/lib/mongodb/mongodb.nit @@ -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) diff --git a/lib/neo4j/neo4j.nit b/lib/neo4j/neo4j.nit index 665ecdb..8c10f6e 100644 --- a/lib/neo4j/neo4j.nit +++ b/lib/neo4j/neo4j.nit @@ -14,16 +14,10 @@ # 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 diff --git a/lib/popcorn/README.md b/lib/popcorn/README.md index bbc9c95..f7c6346 100644 --- a/lib/popcorn/README.md +++ b/lib/popcorn/README.md @@ -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 diff --git a/lib/popcorn/examples/mongodb/example_mongodb.nit b/lib/popcorn/examples/mongodb/example_mongodb.nit index b7de4fe..00b1421 100644 --- a/lib/popcorn/examples/mongodb/example_mongodb.nit +++ b/lib/popcorn/examples/mongodb/example_mongodb.nit @@ -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 diff --git a/lib/popcorn/pop_auth.nit b/lib/popcorn/pop_auth.nit index 2f84be7..3dd71fb 100644 --- a/lib/popcorn/pop_auth.nit +++ b/lib/popcorn/pop_auth.nit @@ -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 diff --git a/lib/popcorn/pop_repos.nit b/lib/popcorn/pop_repos.nit index 72003ec..c054d5a 100644 --- a/lib/popcorn/pop_repos.nit +++ b/lib/popcorn/pop_repos.nit @@ -106,7 +106,7 @@ # # 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}") # diff --git a/lib/postgresql/postgres.nit b/lib/postgresql/postgres.nit index d8a475e..6c209ef 100644 --- a/lib/postgresql/postgres.nit +++ b/lib/postgresql/postgres.nit @@ -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 index 0000000..4ac947a --- /dev/null +++ b/misc/docker/ci-local/README.md @@ -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 index 0000000..aabc9f7 --- /dev/null +++ b/misc/docker/ci-local/docker-compose.yml @@ -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 diff --git a/misc/docker/ci/Dockerfile b/misc/docker/ci/Dockerfile index 0df6ea2..000c0c1 100644 --- a/misc/docker/ci/Dockerfile +++ b/misc/docker/ci/Dockerfile @@ -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) diff --git a/misc/jenkins/checksignedoffby.sh b/misc/jenkins/checksignedoffby.sh index 9a5c27f..41a1342 100755 --- a/misc/jenkins/checksignedoffby.sh +++ b/misc/jenkins/checksignedoffby.sh @@ -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 "" diff --git a/share/android-bdwgc/setup.sh b/share/android-bdwgc/setup.sh index 7ea0c27..334a54b 100755 --- a/share/android-bdwgc/setup.sh +++ b/share/android-bdwgc/setup.sh @@ -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 diff --git a/src/c_tools.nit b/src/c_tools.nit index 9a03c5a..61c1f9c 100644 --- a/src/c_tools.nit +++ b/src/c_tools.nit @@ -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 diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 32dd807..e786374 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -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 diff --git a/src/compiler/compiler_ffi/light.nit b/src/compiler/compiler_ffi/light.nit index 1f59771..ce0f71b 100644 --- a/src/compiler/compiler_ffi/light.nit +++ b/src/compiler/compiler_ffi/light.nit @@ -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 diff --git a/src/ffi/cpp.nit b/src/ffi/cpp.nit index 13a6e92..f660114 100644 --- a/src/ffi/cpp.nit +++ b/src/ffi/cpp.nit @@ -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 diff --git a/src/ffi/extra_java_files.nit b/src/ffi/extra_java_files.nit index e6dfca2..c355441 100644 --- a/src/ffi/extra_java_files.nit +++ b/src/ffi/extra_java_files.nit @@ -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 diff --git a/src/ffi/java.nit b/src/ffi/java.nit index 0dd65be..fe8bc19 100644 --- a/src/ffi/java.nit +++ b/src/ffi/java.nit @@ -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, "", "(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 diff --git a/src/ffi/light_ffi.nit b/src/ffi/light_ffi.nit index 2245a92..b8d572b 100644 --- a/src/ffi/light_ffi.nit +++ b/src/ffi/light_ffi.nit @@ -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 diff --git a/src/ffi/objc.nit b/src/ffi/objc.nit index 9c0545a..24ce15d 100644 --- a/src/ffi/objc.nit +++ b/src/ffi/objc.nit @@ -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 diff --git a/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit b/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit index 44f6854..acd783a 100644 --- a/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit +++ b/src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit @@ -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 diff --git a/src/platform/android.nit b/src/platform/android.nit index 3c85633..9bff506 100644 --- a/src/platform/android.nit +++ b/src/platform/android.nit @@ -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 diff --git a/tests/gitlab_ci.skip b/tests/gitlab_ci.skip index c611822..669601e 100644 --- a/tests/gitlab_ci.skip +++ b/tests/gitlab_ci.skip @@ -1,13 +1,5 @@ -android emscripten -java glsl -mongo mpi -neo objc -postgres -action_nitro -asteronits -nitrpg wiringPi diff --git a/tests/test_curl.nit b/tests/test_curl.nit index 757c963..0d2b4d1 100644 --- a/tests/test_curl.nit +++ b/tests/test_curl.nit @@ -15,15 +15,15 @@ # 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) diff --git a/tests/test_ffi_java_annot_files.nit b/tests/test_ffi_java_annot_files.nit index c57135a..aa87613 100644 --- a/tests/test_ffi_java_annot_files.nit +++ b/tests/test_ffi_java_annot_files.nit @@ -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 diff --git a/tests/test_neo.args b/tests/test_neo.args index a1ee088..46fe939 100644 --- a/tests/test_neo.args +++ b/tests/test_neo.args @@ -1 +1 @@ -localhost 7474 test_prog/test_prog.nit +neo4j 7474 test_prog/test_prog.nit diff --git a/tests/test_neo4j.nit b/tests/test_neo4j.nit index 974a80a..d240047 100644 --- a/tests/test_neo4j.nit +++ b/tests/test_neo4j.nit @@ -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 diff --git a/tests/test_neo4j_batch.nit b/tests/test_neo4j_batch.nit index 464eb2f..499f7ef 100644 --- a/tests/test_neo4j_batch.nit +++ b/tests/test_neo4j_batch.nit @@ -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 diff --git a/tests/test_postgres_native.nit b/tests/test_postgres_native.nit index b7c5a49..d718469 100644 --- a/tests/test_postgres_native.nit +++ b/tests/test_postgres_native.nit @@ -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)") diff --git a/tests/test_postgres_nity.nit b/tests/test_postgres_nity.nit index 4421a02..6714a04 100644 --- a/tests/test_postgres_nity.nit +++ b/tests/test_postgres_nity.nit @@ -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