diff --git a/src/bind/javabind.cpp b/src/bind/javabind.cpp
index 5a870063366a42e387cd8fdc284aebb89211a967..aedd72e13d269bcd40b0646b24b31731eed19a12 100644 (file)
--- a/src/bind/javabind.cpp
+++ b/src/bind/javabind.cpp
/**
- * This is a simple mechanism to bind Inkscape to Java, and thence
+ * @file
+ * @brief This is a simple mechanism to bind Inkscape to Java, and thence
* to all of the nice things that can be layered upon that.
*
* Authors:
#include <errno.h>
#endif
-#if HAVE_SYS_STAT_H
+//--tullarisc #if HAVE_SYS_STAT_H
#include <sys/stat.h>
-#endif
+//#endif
#include "javabind.h"
#include "javabind-private.h"
#include <prefix.h>
#include <glib/gmessages.h>
+//For repr and document
+#include <document.h>
+#include <inkscape.h>
+#include <xml/repr.h>
/**
* Note: We must limit Java or JVM-specific code to this file
* and to dobinding.cpp. It should be hidden from javabind.h
+ *
+ * This file is mostly about getting things up and running, and
+ * providing the basic C-to-Java hooks.
+ *
+ * dobinding.cpp will have the rote and repetitious
+ * class-by-class binding
*/
}
-String getException(JNIEnv *env)
+/**
+ * Convert a java string to a C++ string
+ */
+String getString(JNIEnv *env, jstring jstr)
+{
+ const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
+ String str = chars;
+ env->ReleaseStringUTFChars(jstr, chars);
+ return str;
+}
+
+
+/**
+ * Check if the VM has encountered an Exception. If so, get the String for it
+ * and clear the exception
+ */
+String getExceptionString(JNIEnv *env)
{
String buf;
jthrowable exc = env->ExceptionOccurred();
jclass cls = env->GetObjectClass(exc);
jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
- const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
- buf.append(str);
- env->ReleaseStringUTFChars(jstr, str);
+ buf.append(getString(env, jstr));
env->ExceptionClear();
return buf;
}
-jint getInt(JNIEnv *env, jobject obj, const char *name)
+jint getObjInt(JNIEnv *env, jobject obj, const char *name)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
return env->GetIntField(obj, fid);
}
-void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
+void setObjInt(JNIEnv *env, jobject obj, const char *name, jint val)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
env->SetIntField(obj, fid, val);
}
-jlong getLong(JNIEnv *env, jobject obj, const char *name)
+jlong getObjLong(JNIEnv *env, jobject obj, const char *name)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
return env->GetLongField(obj, fid);
}
-void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
+void setObjLong(JNIEnv *env, jobject obj, const char *name, jlong val)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
env->SetLongField(obj, fid, val);
}
-jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
+jfloat getObjFloat(JNIEnv *env, jobject obj, const char *name)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
return env->GetFloatField(obj, fid);
}
-void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
+void setObjFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
env->SetFloatField(obj, fid, val);
}
-jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
+jdouble getObjDouble(JNIEnv *env, jobject obj, const char *name)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
return env->GetDoubleField(obj, fid);
}
-void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
+void setObjDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
env->SetDoubleField(obj, fid, val);
}
-String getString(JNIEnv *env, jobject obj, const char *name)
+String getObjString(JNIEnv *env, jobject obj, const char *name)
{
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
jstring jstr = (jstring)env->GetObjectField(obj, fid);
- const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
- String str = chars;
- env->ReleaseStringUTFChars(jstr, chars);
- return str;
+ return getString(env, jstr);
}
-void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
+void setObjString(JNIEnv *env, jobject obj, const char *name, const String &val)
{
jstring jstr = env->NewStringUTF(val.c_str());
jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
JavaBinderyImpl::JavaBinderyImpl()
{
- jvm = NULL;
- env = NULL;
+ jvm = NULL;
+ env = NULL;
+ gatewayObj = NULL;
}
JavaBinderyImpl::~JavaBinderyImpl()
-static bool getRegistryString(HKEY root, const char *keyName,
+static bool getRegistryString(HKEY /*root*/, const char *keyName,
const char *valName, char *buf, int buflen)
{
HKEY key;
NULL
};
+
+/**
+ * Return the directory of the .exe that is currently running
+ */
+static String getExePath()
+{
+ char exeName[MAX_PATH+1];
+ GetModuleFileName(NULL, exeName, MAX_PATH);
+ char *slashPos = strrchr(exeName, '\\');
+ if (slashPos)
+ *slashPos = '\0';
+ String s = exeName;
+ return s;
+}
+
+
+/**
+ * Check a directory for several possibilities of sub-locations
+ * under it, where a jvm might exist.
+ */
+static String checkPathUnderRoot(const String &root)
+{
+ for (const char **path = commonJavaPaths ; *path ; path++)
+ {
+ String jpath = root;
+ jpath.append(*path);
+ //msg("trying '%s'", jpath.c_str());
+ struct stat finfo;
+ if (stat(jpath.c_str(), &finfo)>=0)
+ {
+ //msg("found");
+ return jpath;
+ }
+ }
+ return "";
+}
+
+
+
+/**
+ * Attempt to find and load a jvm.dll file. Find the createVM()
+ * function's address and return it
+ */
static CreateVMFunc getCreateVMFunc()
{
bool found = false;
String libname;
/**
- * First, look for JAVA_HOME. This will allow the user
+ * First, look for an embedded jre in the .exe's dir.
+ * This allows us to package our own JRE if we want to.
+ */
+ String inkscapeHome = getExePath();
+ inkscapeHome.append("\\jre");
+ msg("INKSCAPE_HOME='%s'", inkscapeHome.c_str());
+ String path = checkPathUnderRoot(inkscapeHome);
+ if (path.size() > 0)
+ {
+ libname = path;
+ found = true;
+ }
+
+ /**
+ * Next, look for JAVA_HOME. This will allow the user
* to override what's in the registry
- */
- const char *envStr = getenv("JAVA_HOME");
- if (envStr)
+ */
+ if (!found)
{
- String javaHome = cleanPath(envStr);
- msg("JAVA_HOME='%s'", javaHome.c_str());
- for (const char **path = commonJavaPaths ; *path ; path++)
+ const char *envStr = getenv("JAVA_HOME");
+ if (envStr)
{
- String jpath = javaHome;
- jpath.append(*path);
- //msg("trying '%s'", jpath.c_str());
- struct stat finfo;
- if (stat(jpath.c_str(), &finfo)>=0)
+ String javaHome = cleanPath(envStr);
+ msg("JAVA_HOME='%s'", javaHome.c_str());
+ path = checkPathUnderRoot(javaHome);
+ if (path.size() > 0)
{
- //msg("found");
- libname = jpath;
+ libname = path;
found = true;
- break;
}
}
}
return createVM;
}
+/**
+ * Return the directory where the Java classes/libs/resources are
+ * located
+ */
static void getJavaRoot(String &javaroot)
{
- char exeName[80];
- GetModuleFileName(NULL, exeName, 80);
- char *slashPos = strrchr(exeName, '\\');
- if (slashPos)
- *slashPos = '\0';
- javaroot = exeName;
+ /*
+ javaroot = getExePath();
javaroot.append("\\");
- javaroot.append(INKSCAPE_JAVADIR);
+ javaroot.append(INKSCAPE_BINDDIR);
+ javaroot.append("\\java");
+ */
+ javaroot = INKSCAPE_BINDDIR;
+ javaroot.append("\\java");
}
}
+/**
+ * Some common places on a Unix filesystem where JVMs are
+ * often found.
+ */
static const char *commonJavaPaths[] =
{
- "/usr/java",
- "/usr/local/java",
+ "/usr/lib/jvm/jre",
"/usr/lib/jvm",
+ "/usr/local/lib/jvm/jre",
"/usr/local/lib/jvm",
+ "/usr/java",
+ "/usr/local/java",
NULL
};
+
+
/**
* Look for a Java VM (libjvm.so) in several Unix places
*/
+/**
+ * Attempt to find and load a jvm.dll file. Find the createVM()
+ * function's address and return it
+ */
static CreateVMFunc getCreateVMFunc()
{
String libname;
}
+/**
+ * Return the directory where the Java classes/libs/resources are
+ * located
+ */
static void getJavaRoot(String &javaroot)
{
- javaroot = INKSCAPE_JAVADIR;
+ javaroot = INKSCAPE_BINDDIR;
+ javaroot.append("/java");
}
#endif /* !__WIN32__ */
//========================================================================
-// SCRIPT RUNNER
+// Gateway
//========================================================================
/**
- * These methods are used to allow the ScriptRunner class to
- * redirect its stderr and stdout streams to here, to be caught
- * by two string buffers. We can then use those buffers how we
- * want. These native methods are only those needed for running
- * a script. For the main C++/Java bindings, see dobinding.cpp
+ * This is provided to scripts can grab the current copy or the
+ * repr tree. If anyone has a smarter way of doing this, please implement.
*/
-void JNICALL stdOutWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
+jstring JNICALL documentGet(JNIEnv *env, jobject /*obj*/, jlong /*ptr*/)
{
- JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
- bind->stdOut(ch);
+ //JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
+ String buf = sp_repr_save_buf((SP_ACTIVE_DOCUMENT)->rdoc);
+ jstring jstr = env->NewStringUTF(buf.c_str());
+ return jstr;
}
-void JNICALL stdErrWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
+/**
+ * This is provided to scripts can load an XML tree into Inkscape.
+ * If anyone has a smarter way of doing this, please implement.
+ */
+jboolean JNICALL documentSet(JNIEnv */*env*/, jobject /*obj*/, jlong /*ptr*/, jstring /*jstr*/)
{
+ /*
JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
- bind->stdErr(ch);
+ String s = getString(env, jstr);
+ SPDocument *doc = sp_document_new_from_mem(s.c_str(), s.size(), true);
+ */
+ return JNI_TRUE;
}
-
+/**
+ * This method is used to allow the gateway class to
+ * redirect its logging stream here.
+ * For the main C++/Java bindings, see dobinding.cpp
+ */
void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
{
JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
}
-static JNINativeMethod scriptRunnerMethods[] =
+static JNINativeMethod gatewayMethods[] =
{
-{ (char *)"stdOutWrite", (char *)"(JI)V", (void *)stdOutWrite },
-{ (char *)"stdErrWrite", (char *)"(JI)V", (void *)stdErrWrite },
-{ (char *)"logWrite", (char *)"(JI)V", (void *)logWrite },
+{ (char *)"documentGet", (char *)"(J)Ljava/lang/String;", (void *)documentGet },
+{ (char *)"documentSet", (char *)"(JLjava/lang/String;)Z", (void *)documentSet },
+{ (char *)"logWrite", (char *)"(JI)V", (void *)logWrite },
{ NULL, NULL, NULL }
};
+
/**
- * This sets up the 'ScriptRunner' java class for execution of
- * scripts
+ * This sets up the 'Gateway' java class for execution of
+ * scripts. The class's constructor takes a jlong. This java long
+ * is used to store the pointer to 'this'. When ScriptRunner makes
+ * native calls, it passes that jlong back, so that it can call the
+ * methods of this C++ class.
*/
-bool JavaBinderyImpl::setupScriptRunner()
+bool JavaBinderyImpl::setupGateway()
{
- String className = "org/inkscape/cmn/ScriptRunner";
- if (!registerNatives(className, scriptRunnerMethods))
+ String className = "org/inkscape/cmn/Gateway";
+ if (!registerNatives(className, gatewayMethods))
{
return false;
}
jclass cls = env->FindClass(className.c_str());
if (!cls)
{
- err("setupScriptRunner: cannot find class '%s'", className.c_str());
+ err("setupGateway: cannot find class '%s' : %s",
+ className.c_str(), getException().c_str());
return false;
}
jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
if (!mid)
{
- err("setupScriptRunner: cannot find constructor for '%s'", className.c_str());
+ err("setupGateway: cannot find constructor for '%s' : %s",
+ className.c_str(), getException().c_str());
return false;
}
- jobject obj = env->NewObject(cls, mid, ((jlong)this));
- if (!obj)
+ gatewayObj = env->NewObject(cls, mid, ((jlong)this));
+ if (!gatewayObj)
{
- err("setupScriptRunner: cannot construct '%s'", className.c_str());
+ err("setupGateway: cannot construct '%s' : %s",
+ className.c_str(), getException().c_str());
return false;
}
- msg("ScriptRunner ready");
+
+ msg("Gateway ready");
return true;
}
+bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
+{
+ if (!loadJVM())
+ return false;
+
+ std::vector<Value> params;
+ Value langParm(lang);
+ params.push_back(langParm);
+ Value scriptParm(script);
+ params.push_back(scriptParm);
+ Value retval;
+ callInstance(Value::BIND_VOID, gatewayObj, "scriptRun",
+ "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
+ return retval.getBoolean();
+}
+
+bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
+{
+ if (!loadJVM())
+ return false;
+
+ std::vector<Value> params;
+ Value langParm(lang);
+ params.push_back(langParm);
+ Value fnameParm(fname);
+ params.push_back(fnameParm);
+ Value retval;
+ callInstance(Value::BIND_VOID, gatewayObj, "scriptRunFile",
+ "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
+ return retval.getBoolean();
+}
+
+bool JavaBinderyImpl::showConsole()
+{
+ if (!loadJVM())
+ return false;
+
+ std::vector<Value> params;
+ Value retval;
+ callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
+ "()Z", params, retval);
+ return retval.getBoolean();
+}
+
//========================================================================
-// End SCRIPT RUNNER
+// End Gateway
//========================================================================
static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
{
g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
+ return JNI_TRUE;
}
msg("Lib path is: '%s'", libpath.c_str());
JavaVMInitArgs vm_args;
- JavaVMOption options[4];
- options[0].optionString = (char *)classpath.c_str();
- options[1].optionString = (char *)libpath.c_str();
- options[2].optionString = (char *)"-verbose:jni";
- options[3].optionString = (char *)"vfprintf";
- options[3].extraInfo = (void *)vfprintfHook;
- vm_args.version = JNI_VERSION_1_4;
- vm_args.options = options;
- vm_args.nOptions = 4;
- vm_args.ignoreUnrecognized = true;
+ JavaVMOption options[10];//should be enough
+ int nOptions = 0;
+ options[nOptions++].optionString = (char *)classpath.c_str();
+ options[nOptions++].optionString = (char *)libpath.c_str();
+ //options[nOptions++].optionString = (char *)"-verbose:jni";
+ options[nOptions ].optionString = (char *)"vfprintf";
+ options[nOptions++].extraInfo = (void *)vfprintfHook;
+ vm_args.version = JNI_VERSION_1_4;
+ vm_args.options = options;
+ vm_args.nOptions = nOptions;
+ vm_args.ignoreUnrecognized = true;
if (createVM(&jvm, &env, &vm_args) < 0)
{
int versionMinor = (vers ) & 0xffff;
msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
- if (!setupScriptRunner())
+ if (!setupGateway()) {
+ // set jvm = NULL, otherwise, this method will return true when called for the second time while the gateway might not have been created!
+ jvm->DestroyJavaVM();
+ jvm = NULL;
+ env = NULL;
+ err("Java bindings: setupGateway() failed");
return false;
-
+ }
return true;
}
jclass cls = env->FindClass(className.c_str());
if (!cls)
{
- err("Could not find class '%s'", className.c_str());
+ err("Could not find class '%s' : %s",
+ className.c_str(), getException().c_str());
return false;
}
jmethodID mid = env->GetStaticMethodID(cls,
methodName.c_str(), signature.c_str());
if (!mid)
{
- err("Could not find method '%s:%s/%s'", className.c_str(),
- methodName.c_str(), signature.c_str());
+ err("Could not find method '%s:%s/%s' : %s",
+ className.c_str(), methodName.c_str(),
+ signature.c_str(), getException().c_str());
return false;
}
/**
}
case Value::BIND_BOOLEAN:
{
- env->CallStaticBooleanMethodA(cls, mid, jvals);
+ jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
+ if (ret == JNI_TRUE) //remember, don't truncate
+ retval.setBoolean(true);
+ else
+ retval.setBoolean(false);
break;
}
case Value::BIND_INT:
{
- env->CallStaticIntMethodA(cls, mid, jvals);
+ jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
+ retval.setInt(ret);
break;
}
case Value::BIND_DOUBLE:
{
- env->CallStaticDoubleMethodA(cls, mid, jvals);
+ jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
+ retval.setDouble(ret);
break;
}
case Value::BIND_STRING:
{
- env->CallStaticObjectMethodA(cls, mid, jvals);
+ jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
+ jstring jstr = (jstring) ret;
+ const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
+ retval.setString(str);
+ env->ReleaseStringUTFChars(jstr, str);
break;
}
default:
}
}
delete jvals;
- String errStr = getException(env);
+ String errStr = getException();
if (errStr.size()>0)
{
err("callStatic: %s", errStr.c_str());
+/**
+ * Another difficult method. However, this time we are operating
+ * on an existing instance jobject.
+ *
+ * @param type the return type of the method
+ * @param obj the instance upon which to make the call
+ * @param methodName the name of the method being invoked
+ * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
+ * that describes the param and return types of the method.
+ * @param retval the return value of the java method
+ * @return true if the call was successful, else false. This is not
+ * the return value of the method.
+ */
+bool JavaBinderyImpl::callInstance(
+ int type,
+ const jobject obj,
+ const String &methodName,
+ const String &signature,
+ const std::vector<Value> ¶ms,
+ Value &retval)
+{
+ jmethodID mid = env->GetMethodID(env->GetObjectClass(obj),
+ methodName.c_str(), signature.c_str());
+ if (!mid)
+ {
+ err("Could not find method '%s/%s' : %s",
+ methodName.c_str(),
+ signature.c_str(), getException().c_str());
+ return false;
+ }
+ /**
+ * Assemble your parameters into a form usable by JNI
+ */
+ jvalue *jvals = new jvalue[params.size()];
+ for (unsigned int i=0 ; i<params.size() ; i++)
+ {
+ Value v = params[i];
+ switch (v.getType())
+ {
+ case Value::BIND_BOOLEAN:
+ {
+ jvals[i].z = (jboolean)v.getBoolean();
+ break;
+ }
+ case Value::BIND_INT:
+ {
+ jvals[i].i = (jint)v.getInt();
+ break;
+ }
+ case Value::BIND_DOUBLE:
+ {
+ jvals[i].d = (jdouble)v.getDouble();
+ break;
+ }
+ case Value::BIND_STRING:
+ {
+ jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
+ break;
+ }
+ default:
+ {
+ err("Unknown value type: %d", v.getType());
+ return false;
+ }
+ }
+ }
+ switch (type)
+ {
+ case Value::BIND_VOID:
+ {
+ env->CallVoidMethodA(obj, mid, jvals);
+ break;
+ }
+ case Value::BIND_BOOLEAN:
+ {
+ jboolean ret = env->CallBooleanMethodA(obj, mid, jvals);
+ if (ret == JNI_TRUE) //remember, don't truncate
+ retval.setBoolean(true);
+ else
+ retval.setBoolean(false);
+ break;
+ }
+ case Value::BIND_INT:
+ {
+ jint ret = env->CallIntMethodA(obj, mid, jvals);
+ retval.setInt(ret);
+ break;
+ }
+ case Value::BIND_DOUBLE:
+ {
+ jdouble ret = env->CallDoubleMethodA(obj, mid, jvals);
+ retval.setDouble(ret);
+ break;
+ }
+ case Value::BIND_STRING:
+ {
+ jobject ret = env->CallObjectMethodA(obj, mid, jvals);
+ jstring jstr = (jstring) ret;
+ const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
+ retval.setString(str);
+ env->ReleaseStringUTFChars(jstr, str);
+ break;
+ }
+ default:
+ {
+ err("Unknown return type: %d", type);
+ return false;
+ }
+ }
+ delete jvals;
+ String errStr = getException();
+ if (errStr.size()>0)
+ {
+ err("callStatic: %s", errStr.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+/**
+ * Fetch the last exception from the JVM, if any. Clear it to
+ * continue processing
+ *
+ * @return the exception's descriptio,if any. Else ""
+ */
+String JavaBinderyImpl::getException()
+{
+ return getExceptionString(env);
+}
+
+
+
/**
* Convenience method to call the static void main(String argv[])
* method of a given class
*
* @param className full name of the java class
+ * @args the argument strings to the method
* @return true if successful, else false
*/
-bool JavaBinderyImpl::callMain(const String &className)
+bool JavaBinderyImpl::callMain(const String &className,
+ const std::vector<String> &args)
{
std::vector<Value> parms;
+ for (unsigned int i=0 ; i<args.size() ; i++)
+ {
+ Value v;
+ v.setString(args[i]);
+ parms.push_back(v);
+ }
Value retval;
return callStatic(Value::BIND_VOID, className, "main",
"([Ljava/lang/String;)V", parms, retval);
if (!mid)
{
err("Could not get reflect mid for 'getConstructors' : %s",
- getException(env).c_str());
+ getException().c_str());
return false;
}
jobject res = env->CallObjectMethod(cls, mid);
if (!res)
{
- err("Could not get constructors");
+ err("Could not get constructors : %s", getException().c_str());
return false;
}
/**
if (ret < 0)
{
err("Could not register %d native methods for '%s' : %s",
- nrMethods, className.c_str(), getException(env).c_str());
+ nrMethods, className.c_str(), getException().c_str());
return false;
}
return true;