| // 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. |
| |
| #ifndef __JVM_HPP__ |
| #define __JVM_HPP__ |
| |
| #include <jni.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <stout/error.hpp> |
| #include <stout/try.hpp> |
| |
| |
| // Some compilers give us warnings about 'dereferencing type-punned |
| // pointer will break strict-aliasing rules' when we cast our JNIEnv** |
| // to void**. We use this function to do the magic for us. |
| inline void** JNIENV_CAST(JNIEnv** env) |
| { |
| return reinterpret_cast<void**>(env); |
| } |
| |
| |
| struct JNI |
| { |
| enum Version |
| { |
| v_1_1 = JNI_VERSION_1_1, |
| v_1_2 = JNI_VERSION_1_2, |
| v_1_4 = JNI_VERSION_1_4, |
| v_1_6 = JNI_VERSION_1_6 |
| }; |
| }; |
| |
| // Facilitates embedding a JVM and calling into it. |
| class Jvm |
| { |
| public: |
| // Forward declarations. |
| class ConstructorFinder; |
| class MethodFinder; |
| class Constructor; |
| class MethodSignature; |
| class Method; |
| |
| // Starts a new embedded JVM with the given -D options. Each option |
| // supplied should be of the standard form: '-Dproperty=value'. |
| // Returns the singleton Jvm instance or an error if the JVM had |
| // already been created. Note that you can only create one JVM |
| // instance per process since destructing a JVM has issues, see: |
| // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4712793. In |
| // addition, most JVM's use signals and couldn't possibly play |
| // nicely with one another. |
| // TODO(benh): Add a 'create' which just takes an already |
| // constructed JavaVM. This will be useful for when a JVM is calling |
| // into native code versus native code embedding a JVM. |
| // TODO(John Sirois): Consider elevating classpath as a top-level |
| // JVM configuration parameter since it will likely always need to |
| // be specified. Ditto for and non '-X' options. |
| static Try<Jvm*> create( |
| const std::vector<std::string>& options = std::vector<std::string>(), |
| JNI::Version version = JNI::v_1_6, |
| bool exceptions = false); |
| |
| // Returns true if the JVM has already been created. |
| static bool created(); |
| |
| // Returns the singleton JVM instance, creating it with no options |
| // and a default version if necessary. |
| static Jvm* get(); |
| |
| // An opaque class descriptor that can be used to find constructors, |
| // methods and fields. |
| class Class |
| { |
| public: |
| // A factory for new Java reference type class descriptors given |
| // the fully qualified class name (e.g., 'java/io/File'). To |
| // obtain class descriptors for native types (int, short, etc), |
| // use the fields in Jvm. |
| static const Class named(const std::string& name); |
| |
| Class(const Class& that); |
| |
| // Returns the class of an array of the current class. |
| const Class arrayOf() const; |
| |
| // Creates a builder that can be used to locate a constructor of |
| // this class with Jvm::findConstructor. |
| ConstructorFinder constructor() const; |
| |
| // Creates a builder that can be used to locate an instance method |
| // of this class with Jvm::findMethod. |
| MethodFinder method(const std::string& name) const; |
| |
| private: |
| friend class Jvm; |
| |
| Class(const std::string& name, bool native = true); |
| |
| std::string signature() const; |
| |
| std::string name; |
| bool native; |
| }; |
| |
| |
| // A builder that is used to specify a constructor by specifying its |
| // parameter list with zero or more calls to |
| // ConstructorFinder::parameter. |
| class ConstructorFinder |
| { |
| public: |
| // Adds a parameter to the constructor parameter list. |
| ConstructorFinder& parameter(const Class& clazz); |
| |
| private: |
| friend class Class; |
| friend class Jvm; |
| |
| explicit ConstructorFinder(const Class& clazz); |
| |
| const Class clazz; |
| std::vector<Class> parameters; |
| }; |
| |
| |
| // An opaque constructor descriptor that can be used to create new |
| // instances of a class using Jvm::invokeConstructor. |
| class Constructor |
| { |
| public: |
| Constructor(const Constructor& that); |
| |
| private: |
| friend class Jvm; |
| |
| Constructor(const Class& clazz, const jmethodID id); |
| |
| const Class clazz; |
| const jmethodID id; |
| }; |
| |
| |
| // A builder that is used to specify an instance method by |
| // specifying its parameter list with zero or more calls to |
| // MethodFinder::parameter and a final call to MethodFinder::returns |
| // to get an opaque specification of the method for use with |
| // Jvm::findMethod. |
| class MethodFinder |
| { |
| public: |
| // Adds a parameter to the method parameter list. |
| MethodFinder& parameter(const Class& type); |
| |
| // Terminates description of a method by specifying its return type. |
| MethodSignature returns(const Class& type) const; |
| |
| private: |
| friend class Class; |
| |
| MethodFinder(const Class& clazz, const std::string& name); |
| |
| const Class clazz; |
| const std::string name; |
| std::vector<Class> parameters; |
| }; |
| |
| |
| // An opaque method specification for use with Jvm::findMethod. |
| class MethodSignature |
| { |
| public: |
| MethodSignature(const MethodSignature& that); |
| |
| private: |
| friend class Jvm; |
| friend class MethodFinder; |
| |
| MethodSignature(const Class& clazz, |
| const std::string& name, |
| const Class& returnType, |
| const std::vector<Class>& parameters); |
| |
| const Class clazz; |
| const std::string name; |
| const Class returnType; |
| std::vector<Class> parameters; |
| }; |
| |
| |
| // An opaque method descriptor that can be used to invoke instance methods |
| // using Jvm::invokeMethod. |
| class Method |
| { |
| public: |
| Method(const Method& that); |
| |
| private: |
| friend class Jvm; |
| friend class MethodSignature; |
| |
| Method(const Class& clazz, const jmethodID id); |
| |
| const Class clazz; |
| const jmethodID id; |
| }; |
| |
| |
| // An opaque field descriptor that can be used to access fields using |
| // methods like Jvm::getStaticField. |
| class Field |
| { |
| public: |
| Field(const Field& that); |
| |
| private: |
| friend class Jvm; |
| |
| Field(const Class& clazz, const jfieldID id); |
| |
| const Class clazz; |
| const jfieldID id; |
| }; |
| |
| |
| // Base class for all JVM objects. This object "stores" the |
| // underlying global reference and performs the appropriate |
| // operations across copies and assignments. |
| class Object |
| { |
| public: |
| Object() : object(nullptr) {} |
| |
| explicit Object(jobject _object) |
| : object(Jvm::get()->newGlobalRef(_object)) {} |
| |
| Object(const Object& that) |
| : object(nullptr) |
| { |
| if (that.object != nullptr) { |
| object = Jvm::get()->newGlobalRef(that.object); |
| } |
| } |
| |
| ~Object() |
| { |
| if (object != nullptr) { |
| Jvm::get()->deleteGlobalRef(object); |
| } |
| } |
| |
| Object& operator=(const Object& that) |
| { |
| if (object != nullptr) { |
| Jvm::get()->deleteGlobalRef(object); |
| object = nullptr; |
| } |
| if (that.object != nullptr) { |
| object = Jvm::get()->newGlobalRef(that.object); |
| } |
| return *this; |
| } |
| |
| operator jobject() const |
| { |
| return object; |
| } |
| |
| protected: |
| friend class Jvm; // For manipulating object. |
| |
| jobject object; |
| }; |
| |
| |
| class Null : public Object {}; |
| |
| |
| template <typename T, const char* name, const char* signature> |
| class Variable |
| { |
| public: |
| explicit Variable(const Class& _clazz) |
| : clazz(_clazz) |
| { |
| // Check that T extends Object. |
| { T* t = nullptr; Object* o = t; (void) o; } |
| } |
| |
| Variable(const Class& _clazz, const Object& _object) |
| : clazz(_clazz), object(_object) |
| { |
| // Check that T extends Object. |
| { T* t = nullptr; Object* o = t; (void) o; } |
| } |
| |
| // TODO(benh): Implement cast operator (like in StaticVariable). |
| // This requires implementing Jvm::getField too. |
| |
| template <typename U> |
| Variable& operator=(const U& u) |
| { |
| // Check that U extends Object (but not necessarily T since U |
| // might be 'Null'). |
| { U* u = nullptr; Object* o = u; (void) o; } |
| |
| // Note that we actually look up the field lazily (upon first |
| // assignment operator) so that we don't possibly create the JVM |
| // too early. |
| static Field field = Jvm::get()->findField(clazz, name, signature); |
| |
| Jvm::get()->setField<jobject>(object, field, u); |
| |
| return *this; |
| } |
| |
| void bind(const Object& _object) { object = _object; } |
| |
| private: |
| const Class clazz; |
| Object object; // Not const so we can do late binding. |
| }; |
| |
| // Helper for providing access to static variables in a class. You |
| // can use this to delay the variable lookup until it's actually |
| // accessed in order to keep the JVM from getting constructed too |
| // early. See Level in jvm/org/apache/log4j.hpp for an example. |
| // TODO(benh): Provide template specialization for primitive |
| // types (e.g., StaticVariable<int>, StaticVariable<short>, |
| // StaticVariable<std::string>). |
| template <typename T, const char* name, const char* signature> |
| class StaticVariable |
| { |
| public: |
| explicit StaticVariable(const Class& _clazz) |
| : clazz(_clazz) |
| { |
| // Check that T extends Object. |
| { T* t = nullptr; Object* o = t; (void) o; } |
| } |
| |
| operator T() const |
| { |
| // Note that we actually look up the field lazily (upon first |
| // invocation operator) so that we don't possibly create the JVM |
| // too early. |
| static Field field = |
| Jvm::get()->findStaticField(clazz, name, signature); |
| T t; |
| t.object = Jvm::get()->getStaticField<jobject>(field); |
| return t; |
| } |
| |
| private: |
| const Class clazz; |
| }; |
| |
| // Each thread that wants to interact with the JVM needs a JNI |
| // environment which must be obtained by "attaching" to the JVM. We |
| // use the following RAII class to provide the environment and also |
| // make sure a thread is attached and properly detached. Note that |
| // nested constructions are no-ops and use the same environment (and |
| // without detaching too early). |
| // TODO(benh): Support putting a 'Jvm::Env' into a thread-local |
| // variable so we can "attach" to the JVM once. |
| class Env |
| { |
| public: |
| explicit Env(bool daemon = true); |
| ~Env(); |
| |
| JNIEnv* operator->() const { return env; } |
| |
| operator JNIEnv*() const { return env; } |
| |
| private: |
| JNIEnv* env; |
| bool detach; // A nested use of Env should not detach the thread. |
| }; |
| |
| friend class Env; |
| |
| const Class voidClass; |
| const Class booleanClass; |
| const Class byteClass; |
| const Class charClass; |
| const Class shortClass; |
| const Class intClass; |
| const Class longClass; |
| const Class floatClass; |
| const Class doubleClass; |
| const Class stringClass; |
| |
| jstring string(const std::string& s); |
| |
| Constructor findConstructor(const ConstructorFinder& finder); |
| |
| Method findMethod(const MethodSignature& signature); |
| |
| Method findStaticMethod(const MethodSignature& signature); |
| |
| Field findField( |
| const Class& clazz, |
| const std::string& name, |
| const std::string& signature); |
| |
| Field findStaticField( |
| const Class& clazz, |
| const std::string& name, |
| const std::string& signature); |
| |
| // TODO(John Sirois): Add "type checking" to variadic method |
| // calls. Possibly a way to do this with typelists, type |
| // concatenation and unwinding builder inheritance. |
| |
| jobject invoke(const Constructor ctor, ...); |
| |
| template <typename T> |
| T invoke(const jobject receiver, const Method method, ...); |
| |
| template <typename T> |
| T invokeStatic(const Method method, ...); |
| |
| template <typename T> |
| void setField(jobject receiver, const Field& field, T t); |
| |
| template <typename T> |
| T getStaticField(const Field& field); |
| |
| // Checks the exception state of an environment. |
| void check(JNIEnv* env); |
| |
| private: |
| friend class Object; // For creating global references. |
| |
| Jvm(JavaVM* jvm, JNI::Version version, bool exceptions); |
| ~Jvm(); |
| |
| private: |
| jobject newGlobalRef(const jobject object); |
| void deleteGlobalRef(const jobject object); |
| |
| jclass findClass(const Class& clazz); |
| |
| jmethodID findMethod(const Jvm::Class& clazz, |
| const std::string& name, |
| const Jvm::Class& returnType, |
| const std::vector<Jvm::Class>& argTypes, |
| bool isStatic); |
| |
| template <typename T> |
| T invokeV(const jobject receiver, const jmethodID id, va_list args); |
| |
| template <typename T> |
| T invokeStaticV(const Class& receiver, const jmethodID id, va_list args); |
| |
| // Singleton instance. |
| static Jvm* instance; |
| |
| JavaVM* jvm; |
| const JNI::Version version; |
| const bool exceptions; |
| }; |
| |
| |
| template <> |
| void Jvm::invoke<void>(const jobject receiver, const Method method, ...); |
| |
| |
| template <typename T> |
| T Jvm::invoke(const jobject receiver, const Method method, ...) |
| { |
| va_list args; |
| va_start(args, method); |
| const T result = invokeV<T>(receiver, method.id, args); |
| va_end(args); |
| return result; |
| } |
| |
| |
| template <> |
| void Jvm::invokeStatic<void>(const Method method, ...); |
| |
| |
| template <typename T> |
| T Jvm::invokeStatic(const Method method, ...) |
| { |
| va_list args; |
| va_start(args, method); |
| const T result = invokeStaticV<T>(method.clazz, method.id, args); |
| va_end(args); |
| return result; |
| } |
| |
| #endif // __JVM_HPP__ |