summaryrefslogtreecommitdiff
path: root/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/bindings/javahl/native/jniwrapper/jni_base.cpp')
-rw-r--r--subversion/bindings/javahl/native/jniwrapper/jni_base.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
new file mode 100644
index 0000000..e3671a2
--- /dev/null
+++ b/subversion/bindings/javahl/native/jniwrapper/jni_base.cpp
@@ -0,0 +1,382 @@
+/**
+ * @copyright
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ * @endcopyright
+ */
+
+#include <cstring>
+#include <memory>
+#include <apr.h>
+
+#include "jni_env.hpp"
+#include "jni_globalref.hpp"
+#include "jni_exception.hpp"
+#include "jni_object.hpp"
+#include "jni_string.hpp"
+#include "jni_array.hpp"
+#include "jni_stack.hpp"
+
+#include "../JNIUtil.h"
+bool initialize_jni_util(JNIEnv *env);
+
+#include "svn_private_config.h"
+
+
+// Global library initializaiton
+
+/**
+ * Initializer function, called just after the JVM loads the native
+ * library. Stores the global JVM reference and creates the global
+ * class cache.
+ */
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM* jvm, void*)
+{
+ ::Java::Env::static_init(jvm);
+ const ::Java::Env env;
+
+ const apr_status_t status = apr_initialize();
+ if (!status)
+ ::Java::ClassCache::create();
+ else
+ {
+ char buf[2048];
+ std::strcpy(buf, "Could not initialize APR: ");
+ const std::size_t offset = std::strlen(buf);
+ apr_strerror(status, buf + offset, sizeof(buf) - offset - 1);
+ env.ThrowNew(env.FindClass("java/lang/Error"), buf);
+ }
+
+ // Initialize the old-style JavaHL infrastructure.
+ if (!initialize_jni_util(env.get()) && !env.ExceptionCheck())
+ {
+ env.ThrowNew(env.FindClass("java/lang/LinkageError"),
+ "Native library initialization failed");
+ }
+
+ return JNI_VERSION_1_2;
+}
+
+/**
+ * Cleanup function, called just before the JVM unloads the native
+ * library. Destroys the global class cache.
+ */
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM*, void*)
+{
+ ::Java::ClassCache::destroy();
+ apr_terminate();
+}
+
+
+namespace Java {
+
+// class Java::Env
+
+::JavaVM* Env::m_jvm = NULL;
+void Env::static_init(::JavaVM* jvm)
+{
+ m_jvm = jvm;
+}
+
+const char* Env::error_create_global_reference() throw()
+{
+ return _("Could not create global reference");
+}
+
+const char* Env::error_get_contents_string() throw()
+{
+ return _("Could not get contents of Java String");
+}
+
+const char* Env::error_release_null_string() throw()
+{
+ return _("Could not release contents of a null String");
+}
+
+const char* Env::error_create_object_array() throw()
+{
+ return _("Could not create Object array");
+}
+
+namespace {
+// The typed array error messages are always fatal, so allocating
+// the buffer on the heap and never releasing it does not really
+// constitute a memory leak.
+const char* make_typed_error(const char* fmt, const char* type) throw()
+{
+ const apr_size_t bufsize = 512;
+ char *msg = new(std::nothrow) char[bufsize];
+ apr_snprintf(msg, bufsize, fmt, type);
+ return msg;
+}
+} // anonymous namespace
+
+const char* Env::error_create_array(const char* type) throw()
+{
+ return make_typed_error(_("Could not create %sArray"), type);
+}
+
+const char* Env::error_get_contents_array(const char* type) throw()
+{
+ return make_typed_error(_("Could not get %s array contents"), type);
+}
+
+const char* Env::error_release_null_array(const char* type) throw()
+{
+ return make_typed_error(
+ _("Could not release contents of a null %sArray"), type);
+}
+
+::JNIEnv* Env::env_from_jvm()
+{
+ if (m_jvm)
+ {
+ void *penv;
+ switch (m_jvm->GetEnv(&penv, JNI_VERSION_1_2))
+ {
+ case JNI_OK:
+ return static_cast<JNIEnv*>(penv);
+
+ case JNI_EDETACHED:
+ throw std::runtime_error(
+ _("Native thread is not attached to a Java VM"));
+
+ case JNI_EVERSION:
+ throw std::runtime_error(_("Unsupported JNI version"));
+
+ default:
+ throw std::runtime_error(_("Invalid JNI environment"));
+ }
+ }
+ throw std::logic_error(_("JavaVM instance was not initialized"));
+}
+
+void Env::throw_java_out_of_memory(const char* message) const
+{
+ OutOfMemoryError(*this).raise(message);
+}
+
+// class Java::LocalFrame
+
+const jint LocalFrame::DEFAULT_CAPACITY = 16;
+
+
+// class Java::GlobalObject
+
+GlobalObject& GlobalObject::operator=(jobject that)
+{
+ this->~GlobalObject();
+ return *new(this) GlobalObject(Env(), that);
+}
+
+GlobalObject::~GlobalObject()
+{
+ if (m_obj)
+ Env().DeleteGlobalRef(m_obj);
+}
+
+
+// class Java::GlobalClass
+
+GlobalClass& GlobalClass::operator=(jclass that)
+{
+ this->~GlobalClass();
+ return *new(this) GlobalClass(Env(), that);
+}
+
+
+// Class Java::Object
+
+const char* const Object::m_class_name = "java/lang/Object";
+Object::ClassImpl::~ClassImpl() {}
+
+
+// Class Java::Class
+
+const char* const Class::m_class_name = "java/lang/Class";
+Class::ClassImpl::~ClassImpl() {}
+
+MethodID Class::m_mid_get_class;
+MethodID Class::m_mid_get_name;
+
+void Class::static_init(Env env, jclass cls)
+{
+ m_mid_get_class = env.GetMethodID(
+ ClassCache::get_object(env)->get_class(),
+ "getClass", "()Ljava/lang/Class;");
+ m_mid_get_name = env.GetMethodID(
+ cls, "getName", "()Ljava/lang/String;");
+}
+
+namespace{
+jobject get_class_of_object(Env env, jobject obj, jmethodID mid_get_class)
+{
+ if (!obj)
+ return NULL;
+ return env.CallObjectMethod(obj, mid_get_class);
+}
+} // anonymous namespace
+
+Class::Class(Env env, jobject obj)
+ : m_env(env),
+ m_jthis(get_class_of_object(env, obj, m_mid_get_class))
+{}
+
+Class::Class(const Object& obj)
+ : m_env(obj.get_env()),
+ m_jthis(get_class_of_object(obj.get_env(), obj.get(), m_mid_get_class))
+{}
+
+jstring Class::get_name() const
+{
+ if (!m_jthis)
+ return NULL;
+ return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_name));
+}
+
+
+// Class Java::String
+
+const char* const String::m_class_name = "java/lang/String";
+String::ClassImpl::~ClassImpl() {}
+
+const char* String::strdup(apr_pool_t* pool) const
+{
+ return apr_pstrdup(pool, String::Contents(*this).c_str());
+}
+
+void String::MutableContents::set_value(const char* new_text)
+{
+ if (!m_new_text)
+ throw std::invalid_argument(
+ _("Cannot set String contents to null"));
+ if (m_text)
+ {
+ m_new_text = new_text;
+ m_length = jsize(::std::strlen(new_text));
+ }
+ else
+ throw std::logic_error(
+ _("Cannot change the contents of a null String"));
+}
+
+// class Java::Exception
+
+void Exception::throw_java_exception() const
+{
+ if (instantiated()
+ ? m_env.Throw(m_jthis)
+ : m_env.ThrowNew(m_class, NULL))
+ throw std::runtime_error(_("Could not throw Java exception"));
+}
+
+void Exception::throw_java_exception(const char* message) const
+{
+ if (m_env.ThrowNew(m_class, message))
+ throw std::runtime_error(_("Could not throw Java exception"));
+}
+
+jstring Exception::get_message() const
+{
+ if (instantiated())
+ return jstring(m_env.CallObjectMethod(m_jthis, m_mid_get_message));
+ throw std::logic_error(_("Could not get exception message:"
+ " Exception instance is not available"));
+}
+
+const char* const Exception::m_class_name = "java/lang/Throwable";
+Exception::ClassImpl::~ClassImpl() {}
+
+MethodID Exception::m_mid_get_message;
+void Exception::static_init(Env env, jclass cls)
+{
+ m_mid_get_message = env.GetMethodID(
+ cls, "getMessage", "()Ljava/lang/String;");
+}
+
+// Other exception class initializers
+
+const char* const RuntimeException::m_class_name =
+ "java/lang/RuntimeException";
+
+const char* const NullPointerException::m_class_name =
+ "java/lang/NullPointerException";
+
+const char* const OutOfMemoryError::m_class_name =
+ "java/lang/OutOfMemoryError";
+
+const char* const IndexOutOfBoundsException::m_class_name =
+ "java/lang/IndexOutOfBoundsException";
+IndexOutOfBoundsException::ClassImpl::~ClassImpl() {}
+
+const char* const IOException::m_class_name =
+ "java/io/IOException";
+
+const char* const IllegalArgumentException::m_class_name =
+ "java/lang/IllegalArgumentException";
+
+const char *const NoSuchElementException::m_class_name =
+ "java/util/NoSuchElementException";
+NoSuchElementException::ClassImpl::~ClassImpl() {}
+
+// Implementation of jni_stack.hpp
+
+void handle_svn_error(Env env, ::svn_error_t* err)
+{
+ jthrowable cause = NULL;
+
+ // If the exception being currently thrown was generated by the
+ // JavaHL bindings, then assume the error was propagated through
+ // native code and do not re-throw it.
+ if (env.ExceptionCheck())
+ {
+ cause = env.ExceptionOccurred();
+ if (env.IsInstanceOf(
+ cause, ClassCache::get_subversion_exception(env)->get_class()))
+ {
+ // XXX FIXME: Should really have a special error code
+ // specifically for propagating Java exceptions from
+ // callbacks through native code.
+ svn_error_clear(err);
+ throw SignalExceptionThrown();
+ }
+ }
+
+ // Make sure there's only a single exception in the environment.
+ if (cause)
+ env.ExceptionClear();
+
+ ::JNIUtil::handleSVNError(err, cause);
+ throw SignalExceptionThrown();
+}
+
+const char* unknown_cxx_exception_message() throw()
+{
+ return _("Caught unknown C++ exception");
+}
+
+svn_error_t* caught_java_exception_error(apr_status_t status) throw()
+{
+ return svn_error_create(status, JNIUtil::wrapJavaException(),
+ _("Java exception"));
+}
+
+} // namespace Java