Fix the reason that the catalog is currenlty broken (maybe ironicaly related to #1816)
Pull-Request: #1824
#ifdef ANDROID
#include <android/log.h>
#define PRINT_ERROR(...) ((void)__android_log_print(ANDROID_LOG_WARN, "nit", __VA_ARGS__))
+
+ // FIXME bring back when the GC is fixed in Android
+ #undef WITH_LIBGC
#else
#define PRINT_ERROR(...) ((void)fprintf(stderr, __VA_ARGS__))
#endif
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android implementation of `app:http_request`
+module http_request is
+ android_manifest """<uses-permission android:name="android.permission.INTERNET" />"""
+end
+
+intrude import app::http_request
+import ui
+
+in "Java" `{
+ import org.apache.http.client.methods.HttpGet;
+ import org.apache.http.impl.client.DefaultHttpClient;
+ import org.apache.http.HttpResponse;
+ import org.apache.http.HttpStatus;
+ import org.apache.http.StatusLine;
+ import java.io.ByteArrayOutputStream;
+`}
+
+redef class App
+ redef fun run_on_ui_thread(task) do app.native_activity.run_on_ui_thread task
+end
+
+redef class Text
+
+ redef fun http_get
+ do
+ jni_env.push_local_frame 8
+ var juri = self.to_java_string
+ var jrep = java_http_get(juri)
+
+ assert not jrep.is_java_null
+
+ var res
+ if jrep.is_exception then
+ jrep = jrep.as_exception
+ res = new HttpRequestResult(null, new IOError(jrep.message.to_s))
+ else if jrep.is_http_response then
+ jrep = jrep.as_http_response
+ res = new HttpRequestResult(jrep.content.to_s, null, jrep.status_code)
+ else abort
+
+ jni_env.pop_local_frame
+ return res
+ end
+end
+
+redef class AsyncHttpRequest
+
+ redef fun main
+ do
+ var res = super
+ jvm.detach_current_thread
+ return res
+ end
+end
+
+redef class JavaObject
+ private fun is_exception: Bool in "Java" `{ return self instanceof Exception; `}
+ private fun as_exception: JavaException in "Java" `{ return (Exception)self; `}
+
+ private fun is_http_response: Bool in "Java" `{ return self instanceof HttpResponse; `}
+ private fun as_http_response: JavaHttpResponse in "Java" `{ return (HttpResponse)self; `}
+end
+
+private fun java_http_get(uri: JavaString): JavaObject in "Java" `{
+ try {
+ DefaultHttpClient client = new DefaultHttpClient();
+ HttpGet get = new HttpGet(uri);
+ return client.execute(get);
+ } catch (Exception ex) {
+ return ex;
+ }
+`}
+
+private extern class JavaHttpResponse in "Java" `{ org.apache.http.HttpResponse `}
+ super JavaObject
+
+ fun status_code: Int in "Java" `{ return self.getStatusLine().getStatusCode(); `}
+
+ fun content: JavaString in "Java" `{
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ self.getEntity().writeTo(out);
+ out.close();
+ return out.toString();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return "";
+ }
+ `}
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# HTTP request services: `AsyncHttpRequest` and `Text::http_get`
+module http_request
+
+import app_base
+import pthreads
+import json::serialization
+
+import linux::http_request is conditional(linux)
+import android::http_request is conditional(android)
+
+redef class App
+ # Platform specific service to execute `task` on the main/UI thread
+ fun run_on_ui_thread(task: Task) is abstract
+end
+
+# Thread executing an HTTP request and deserializing JSON asynchronously
+#
+# This class defines four methods acting on the main/UI thread,
+# they should be implemented as needed:
+# * before
+# * on_load
+# * on_fail
+# * after
+class AsyncHttpRequest
+ super Thread
+
+ # Root URI of the remote server
+ fun rest_server_uri: String is abstract
+
+ # Action, or path, for this request within the `rest_server_uri`
+ fun rest_action: String is abstract
+
+ # Should the response content be deserialized from JSON?
+ var deserialize_json = true is writable
+
+ redef fun start
+ do
+ before
+ super
+ end
+
+ redef fun main
+ do
+ var uri = rest_server_uri / rest_action
+
+ # Execute REST request
+ var rep = uri.http_get
+ if rep.is_error then
+ app.run_on_ui_thread new RestRunnableOnFail(self, rep.error)
+ return null
+ end
+
+ if not deserialize_json then
+ app.run_on_ui_thread new RestRunnableOnLoad(self, rep)
+ return null
+ end
+
+ # Deserialize
+ var deserializer = new JsonDeserializer(rep.value)
+ var res = deserializer.deserialize
+ if deserializer.errors.not_empty then
+ app.run_on_ui_thread new RestRunnableOnFail(self, deserializer.errors.first)
+ end
+
+ app.run_on_ui_thread new RestRunnableOnLoad(self, res)
+ return null
+ end
+
+ # Prepare the UI or other parts of the program before executing the REST request
+ fun before do end
+
+ # Invoked when the HTTP request returned valid data
+ #
+ # If `deserialize_json`, the default behavior, this method is invoked only if deserialization was successful.
+ # In this case, `result` may be any deserialized object.
+ #
+ # Otherwise, if `not deserialize_json`, `result` contains the content of the response as a `String`.
+ fun on_load(result: nullable Object) do end
+
+ # Invoked when the HTTP request has failed and no data was received or deserialization failed
+ fun on_fail(error: Error) do print_error "REST request '{rest_action}' failed with: {error}"
+
+ # Complete this request whether it was a success or not
+ fun after do end
+end
+
+redef class Text
+ # Execute an HTTP GET request synchronously at the URI `self`
+ #
+ # ~~~nitish
+ # var response = "http://example.org/".http_get
+ # if response.is_error then
+ # print_error response.error
+ # else
+ # print "HTTP status code: {response.code}"
+ # print response.value
+ # end
+ # ~~~
+ private fun http_get: HttpRequestResult is abstract
+end
+
+# Result of a call to `Text::http_get`
+#
+# Users should first check if `is_error` to use `error`.
+# Otherwise they can use `value` to get the content of the response
+# and `code` for the HTTP status code.
+class HttpRequestResult
+ super MaybeError[String, Error]
+
+ # The HTTP status code, if any
+ var maybe_code: nullable Int
+
+ # The status code
+ # Require: `not is_error`
+ fun code: Int do return maybe_code.as(not null)
+end
+
+private abstract class HttpRequestTask
+ super Task
+
+ # `AsyncHttpRequest` to which send callbacks
+ var sender_thread: AsyncHttpRequest
+end
+
+private class RestRunnableOnLoad
+ super HttpRequestTask
+
+ var res: nullable Object
+
+ redef fun main
+ do
+ sender_thread.on_load(res)
+ sender_thread.after
+ end
+end
+
+private class RestRunnableOnFail
+ super HttpRequestTask
+
+ var error: Error
+
+ redef fun main
+ do
+ sender_thread.on_fail(error)
+ sender_thread.after
+ end
+end
# REQUIRE: `not is_error`
fun value: V do return maybe_value.as(V)
- # The require
+ # The error
# REQUIRE: `is_error`
fun error: E do return maybe_error.as(E)
import gtk_core
`{
-#ifdef GdkCallback_run
- // Callback to GdkCallaback::run
- gboolean nit_gdk_callback(gpointer user_data) {
- GdkCallback_decr_ref(user_data);
- return GdkCallback_run(user_data);
+#ifdef Task_gdk_main
+ // Callback to Task::gdk_main
+ gboolean nit_gdk_callback_task(gpointer user_data) {
+ Task_decr_ref(user_data);
+ return Task_gdk_main(user_data);
}
#endif
`}
-# Callback to pass to `gdk_threads_add_idle`
-class GdkCallback
+redef class Task
# Small unit of code executed by the GDK loop when idle
#
- # Returns true if this object should be invoked again.
- fun run: Bool do return false
+ # Returns `true` if this object should be invoked again.
+ fun gdk_main: Bool
+ do
+ main
+ return false
+ end
end
# Add a callback to execute whenever there are no higher priority events pending
-fun gdk_threads_add_idle(callback: GdkCallback): Int import GdkCallback.run `{
- GdkCallback_incr_ref(callback);
- return gdk_threads_add_idle(&nit_gdk_callback, callback);
+fun gdk_threads_add_idle(task: Task): Int import Task.gdk_main `{
+ Task_incr_ref(task);
+ return gdk_threads_add_idle(&nit_gdk_callback_task, task);
`}
return to_java_string.to_s
end
end
+
+# Java class: java.lang.Throwable
+extern class JavaThrowable in "Java" `{ java.lang.Throwable `}
+ super JavaObject
+
+ # Java implementation: java.lang.String java.lang.Throwable.getMessage()
+ fun message: JavaString in "Java" `{
+ return self.getMessage();
+ `}
+
+ # Java implementation: java.lang.String java.lang.Throwable.getLocalizedMessage()
+ fun localized_message: JavaString in "Java" `{
+ return self.getLocalizedMessage();
+ `}
+
+ # Java implementation: java.lang.Throwable.printStackTrace()
+ fun print_stack_trace in "Java" `{
+ self.printStackTrace();
+ `}
+
+ # Java implementation: java.lang.Throwable java.lang.Throwable.getCause()
+ fun cause: JavaThrowable in "Java" `{
+ return self.getCause();
+ `}
+
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = JavaThrowable_sys(self);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, self);
+ `}
+
+ redef fun pop_from_local_frame_with_env(jni_env) `{
+ return (*jni_env)->PopLocalFrame(jni_env, self);
+ `}
+end
+
+# Java class: java.lang.Exception
+extern class JavaException in "Java" `{ java.lang.Exception `}
+ super JavaThrowable
+
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = JavaException_sys(self);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, self);
+ `}
+
+ redef fun pop_from_local_frame_with_env(jni_env) `{
+ return (*jni_env)->PopLocalFrame(jni_env, self);
+ `}
+end
end
end
+redef class Text
+
+ # Deserialize a `nullable Object` from this JSON formatted string
+ #
+ # Warning: Deserialization errors are reported with `print_error` and
+ # may be returned as a partial object or as `null`.
+ #
+ # This method is not appropriate when errors need to be handled programmatically,
+ # manually use a `JsonDeserializer` in such cases.
+ fun from_json_string: nullable Object
+ do
+ var deserializer = new JsonDeserializer(self)
+ var res = deserializer.deserialize
+ if deserializer.errors.not_empty then
+ print_error "Deserialization Errors: {deserializer.errors.join(", ")}"
+ end
+ return res
+ end
+end
+
redef class Serializable
private fun serialize_to_json(v: JsonSerializer)
do
v.stream.write "\}"
end
+ # Serialize this object to a JSON string with metadata for deserialization
+ fun to_json_string: String
+ do
+ var stream = new StringWriter
+ var serializer = new JsonSerializer(stream)
+ serializer.serialize self
+ stream.close
+ return stream.to_s
+ end
+
# Serialize this object to plain JSON
#
# This is a shortcut using `JsonSerializer::plain_json`,
end
end
- redef init from_deserializer(v: Deserializer)
+ redef init from_deserializer(v)
do
super
if v isa JsonDeserializer then
end
end
- # Instantiate a new `Array` from its serialized representation.
- redef init from_deserializer(v: Deserializer)
+ redef init from_deserializer(v)
do
super
}
return env;
`}
+
+ # Detach the calling thread from this JVM
+ fun detach_current_thread import jni_error `{
+ int res = (*self)->DetachCurrentThread(self);
+ if (res != JNI_OK) {
+ JavaVM_jni_error(NULL, "Could not detach current thread to Java VM", res);
+ }
+ `}
end
# Represents a jni JNIEnv, which is a thread in a JavaVM
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Implementation of `app::http_request` using GDK and Curl
+module http_request
+
+intrude import app::http_request
+private import curl
+private import gtk::gdk
+
+redef class App
+ redef fun run_on_ui_thread(task) do gdk_threads_add_idle task
+end
+
+redef class Text
+ redef fun http_get
+ do
+ var req = new CurlHTTPRequest(to_s)
+ var rep = req.execute
+ if rep isa CurlResponseSuccess then
+ return new HttpRequestResult(rep.body_str, null, rep.status_code)
+ else
+ assert rep isa CurlResponseFailed
+ var error = new IOError(rep.error_msg)
+ return new HttpRequestResult(null, error)
+ end
+ end
+end
v.serialize_attribute("item", first)
end
end
+
+redef class Error
+ super Serializable
+
+ redef init from_deserializer(v)
+ do
+ v.notify_of_creation self
+
+ var message = v.deserialize_attribute("message")
+ if not message isa String then message = ""
+ init message
+
+ var cause = v.deserialize_attribute("cause")
+ if cause isa nullable Error then self.cause = cause
+ end
+
+ redef fun core_serialize_to(v)
+ do
+ v.serialize_attribute("message", message)
+ v.serialize_attribute("cause", cause)
+ end
+end
v.add_decl("int main(int argc, char** argv) \{")
end
+ v.add "#ifndef ANDROID"
v.add("signal(SIGABRT, sig_handler);")
v.add("signal(SIGFPE, sig_handler);")
v.add("signal(SIGILL, sig_handler);")
v.add("signal(SIGINT, sig_handler);")
v.add("signal(SIGTERM, sig_handler);")
v.add("signal(SIGSEGV, sig_handler);")
+ v.add "#endif"
v.add("signal(SIGPIPE, SIG_IGN);")
v.add("glob_argc = argc; glob_argv = argv;")
v.add_abort("type null")
v.add("\}")
v.add("if({t}->table_size < 0) \{")
- v.add("PRINT_ERROR(\"Insantiation of a dead type: %s\\n\", {t}->name);")
+ v.add("PRINT_ERROR(\"Instantiation of a dead type: %s\\n\", {t}->name);")
v.add_abort("type dead")
v.add("\}")
end
for cb in callbacks do
jni_methods.add_all(cb.jni_methods_declaration(self))
end
+ for cb in callbacks_used_from_java.types do
+ jni_methods.add_all(cb.jni_methods_declaration(self))
+ end
var cf = new CFunction("void nit_ffi_with_java_register_natives(JNIEnv* env, jclass jclazz)")
cf.exprs.add """
# Used by `JavaLanguage::compile_extern_method` when calling JNI's `CallStatic*Method`.
# This strategy is used by JNI to type the return of callbacks to Java.
private fun jni_signature_alt: String do return "Int"
+
+ redef fun compile_callback_to_java(mmodule, mainmodule, ccu)
+ do
+ var java_file = mmodule.java_file
+ if java_file == null 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 cf = new CFunction("JNIEXPORT {csignature}")
+ cf.exprs.add "\tnitni_global_ref_{variation}((void*)(long)object);"
+ ccu.add_non_static_local_function cf
+
+ # Java
+ java_file.class_content.add "private native static void {friendly_name}(int object);\n"
+ end
+ end
+
+ redef fun jni_methods_declaration(from_mmodule)
+ do
+ var arr = new Array[String]
+ for variation in ["incr", "decr"] do
+ var friendly_name = "{mangled_cname}_{variation}_ref"
+ var jni_format = "(I)V"
+ var cname = "{from_mmodule.impl_java_class_name}_{friendly_name}"
+ arr.add """{"{{{friendly_name}}}", "{{{jni_format}}}", {{{cname}}}}"""
+ end
+
+ return arr
+ end
end
redef class MClassType
if mtype isa MGenericType and
mtype.is_subtype(m, null, serializable_type) and
mtype.is_visible_from(mmodule) and
+ mtype.mclass.kind == concrete_kind and
not compiled_types.has(mtype) then
compiled_types.add mtype
redef fun name do return "android"
- redef fun supports_libgc do return true
+ redef fun supports_libgc do return false
redef fun supports_libunwind do return false
## Generate Application.mk
dir = "{android_project_root}/jni/"
"""
-APP_ABI := armeabi armeabi-v7a x86 mips
+APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-{{{app_target_api}}}
""".write_to_file "{dir}/Application.mk"
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import java
+
+class A
+ fun foo in "Java" `{
+ A_incr_ref(self);
+ System.out.println("a");
+ A_decr_ref(self);
+ System.out.println("b");
+ `}
+end
+
+var a = new A
+a.foo