forked from s-u/rJava
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrJava.c
More file actions
222 lines (199 loc) · 7.22 KB
/
rJava.c
File metadata and controls
222 lines (199 loc) · 7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <R.h>
#include <Rversion.h>
#include <Rdefines.h>
#include "rJava.h"
#include <stdlib.h>
#include <string.h>
/* determine whether eenv chache should be used (has no effect if JNI_CACHE is not set) */
int use_eenv = 1;
/* cached environment. Do NOT use directly! Always use getJNIEnv()! */
JNIEnv *eenv;
/* -- hack to get at the current call from C code using contexts */
#if ( R_VERSION >= R_Version(1, 7, 0) )
#include <setjmp.h>
/* stuff we need to pull for Windows... */
#ifdef WIN32
/* this is from gnuwin32/fixed/h/psignal.h */
#ifndef _SIGSET_T_
#define _SIGSET_T_
typedef int sigset_t;
#endif /* Not _SIGSET_T_ */
typedef struct
{
jmp_buf jmpbuf; /* Calling environment. */
int mask_was_saved; /* Saved the signal mask? */
sigset_t saved_mask; /* Saved signal mask. */
} sigjmp_buf[1];
/* we need to set HAVE_POSIX_SETJMP since we don't have config.h on Win */
#ifndef HAVE_POSIX_SETJMP
#define HAVE_POSIX_SETJMP
#endif
#endif
#ifdef HAVE_POSIX_SETJMP
#define JMP_BUF sigjmp_buf
#else
#define JMP_BUF jmp_buf
#endif
#ifndef CTXT_BUILTIN
#define CTXT_BUILTIN 64
#endif
typedef struct RCNTXT { /* this RCNTXT structure is only partial since we need to get at "call" - it is safe form R 1.7.0 on */
struct RCNTXT *nextcontext; /* The next context up the chain <<-- we use this one to skip the .Call/.External call frame */
int callflag; /* The context "type" <<<-- we use this one to skip the .Call/.External call frame */
JMP_BUF cjmpbuf; /* C stack and register information */
int cstacktop; /* Top of the pointer protection stack */
int evaldepth; /* evaluation depth at inception */
SEXP promargs; /* Promises supplied to closure */
SEXP callfun; /* The closure called */
SEXP sysparent; /* environment the closure was called from */
SEXP call; /* The call that effected this context <<<--- we pass this one to the condition */
SEXP cloenv; /* The environment */
} RCNTXT;
#ifndef LibExtern
#define LibExtern extern
#endif
LibExtern RCNTXT* R_GlobalContext;
static SEXP getCurrentCall() {
RCNTXT *ctx = R_GlobalContext;
/* skip the .External/.Call context to get at the underlying call */
if (ctx->nextcontext && (ctx->callflag & CTXT_BUILTIN))
ctx = ctx->nextcontext;
/* skip .jcheck */
if (TYPEOF(ctx->call) == LANGSXP && CAR(ctx->call) == install(".jcheck") && ctx->nextcontext)
ctx = ctx->nextcontext;
return ctx->call;
}
#else
static SEXP getCurrentCall() {
return R_NilValue;
}
#endif
/* -- end of hack */
/** throw an exception using R condition code.
* @param msg - message string
* @param jobj - jobjRef object of the exception
* @param clazzes - simple name of all the classes in the inheritance tree of the exception plus "error" and "condition"
*/
HIDE void throwR(SEXP msg, SEXP jobj, SEXP clazzes) {
SEXP cond = PROTECT(allocVector(VECSXP, 3));
SEXP names = PROTECT(allocVector(STRSXP, 3));
SET_VECTOR_ELT(cond, 0, msg);
SET_VECTOR_ELT(cond, 1, getCurrentCall());
SET_VECTOR_ELT(cond, 2, jobj);
SET_STRING_ELT(names, 0, mkChar("message"));
SET_STRING_ELT(names, 1, mkChar("call"));
SET_STRING_ELT(names, 2, mkChar("jobj"));
setAttrib(cond, R_NamesSymbol, names);
setAttrib(cond, R_ClassSymbol, clazzes);
UNPROTECT(2); /* clazzes, names */
eval(LCONS(install("stop"), CONS(cond, R_NilValue)), R_GlobalEnv);
UNPROTECT(1); /* cond */
}
/* check for exceptions and throw them to R level */
HIDE void ckx(JNIEnv *env) {
SEXP xr, xobj, msg = 0, xclass = 0; /* note: we don't bother counting protections becasue we never return */
jthrowable x = 0;
if (env && !(x = (*env)->ExceptionOccurred(env))) return;
if (!env) {
env = getJNIEnv();
if (!env)
error("Unable to retrieve JVM environment.");
ckx(env);
return;
}
/* env is valid and an exception occurred */
/* we create the jobj first, because the exception may in theory disappear after being cleared,
yet this can be (also in theory) risky as it uses further JNI calls ... */
xobj = j2SEXP(env, x, 0);
(*env)->ExceptionClear(env);
/* grab the list of class names (without package path) */
SEXP clazzes = PROTECT( getSimpleClassNames_asSEXP( (jobject)x, (jboolean)1 ) ) ;
/* ok, now this is a critical part that we do manually to avoid recursion */
{
jclass cls = (*env)->GetObjectClass(env, x);
if (cls) {
jstring cname;
jmethodID mid = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
if (mid) {
jstring s = (jstring)(*env)->CallObjectMethod(env, x, mid);
if (s) {
const char *c = (*env)->GetStringUTFChars(env, s, 0);
if (c) {
msg = PROTECT(mkString(c));
(*env)->ReleaseStringUTFChars(env, s, c);
}
}
}
/* beside toString() we also need to call getName() on cls to get the subclass */
cname = (jstring) (*env)->CallObjectMethod(env, cls, mid_getName);
if (cname) {
const char *c = (*env)->GetStringUTFChars(env, cname, 0);
if (c) {
/* convert full class name to JNI notation */
char *cn = strdup(c), *d = cn;
while (*d) { if (*d == '.') *d = '/'; d++; }
xclass = mkString(cn);
free(cn);
(*env)->ReleaseStringUTFChars(env, cname, c);
}
(*env)->DeleteLocalRef(env, cname);
}
if ((*env)->ExceptionOccurred(env))
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, cls);
} else (*env)->ExceptionClear(env);
if (!msg)
msg = PROTECT(mkString("Java Exception <no description because toString() failed>"));
}
/* delete the local reference to the exception (jobjRef has a global copy) */
(*env)->DeleteLocalRef(env, x);
/* construct the jobjRef */
xr = PROTECT(NEW_OBJECT(MAKE_CLASS("jobjRef")));
if (inherits(xr, "jobjRef")) {
SET_SLOT(xr, install("jclass"), xclass ? xclass : mkString("java/lang/Throwable"));
SET_SLOT(xr, install("jobj"), xobj);
}
/* and off to R .. (we're keeping xr and clazzes protected) */
throwR(msg, xr, clazzes);
/* throwR never returns so don't even bother ... */
}
/* clear any pending exceptions */
HIDE void clx(JNIEnv *env) {
if (env && (*env)->ExceptionOccurred(env))
(*env)->ExceptionClear(env);
}
#ifdef JNI_CACHE
HIDE JNIEnv *getJNIEnvSafe();
HIDE JNIEnv *getJNIEnv() {
return (use_eenv)?eenv:getJNIEnvSafe();
}
HIDE JNIEnv *getJNIEnvSafe()
#else
HIDE JNIEnv *getJNIEnv()
#endif
{
JNIEnv *env;
jsize l;
jint res;
if (!jvm) { /* we're hoping that the JVM pointer won't change :P we fetch it just once */
res = JNI_GetCreatedJavaVMs(&jvm, 1, &l);
if (res != 0) {
error("JNI_GetCreatedJavaVMs failed! (result:%d)",(int)res); return 0;
}
if (l < 1)
error("No running JVM detected. Maybe .jinit() would help.");
if (!rJava_initialized)
error("rJava was called from a running JVM without .jinit().");
}
res = (*jvm)->AttachCurrentThread(jvm, (void**) &env, 0);
if (res!=0) {
error("AttachCurrentThread failed! (result:%d)", (int)res); return 0;
}
if (env && !eenv) eenv=env;
/* if (eenv!=env)
fprintf(stderr, "Warning! eenv=%x, but env=%x - different environments encountered!\n", eenv, env); */
return env;
}
REP void RuseJNICache(int *flag) {
if (flag) use_eenv=*flag;
}