xref: /illumos-gate/usr/src/lib/libdtrace_jni/common/dtj_util.c (revision 1da57d551424de5a9d469760be7c4b4d4f10a755)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <sys/types.h>
30 #include <pthread.h>
31 #include <string.h>
32 #include <dtj_util.h>
33 
34 /*
35  * dtj_util.c separates functionality that is generally useful from
36  * that which is specific to the Java DTrace API.  If moved to a separate
37  * library, this functionality could be shared by other JNI wrappers.
38  */
39 
40 boolean_t g_dtj_util_debug = B_FALSE;
41 static boolean_t g_dtj_load_common = B_FALSE;
42 
43 /* NativeException */
44 jclass g_nx_jc = 0;
45 jmethodID g_nxinit_jm = 0;
46 
47 /* java.io.Serializable */
48 jclass g_serial_jc = 0;
49 
50 /* java.lang.Number */
51 jclass g_number_jc = 0;
52 jmethodID g_shortval_jm = 0;
53 jmethodID g_intval_jm = 0;
54 jmethodID g_longval_jm = 0;
55 
56 /* java.lang.Byte */
57 jclass g_byte_jc = 0;
58 jmethodID g_byteinit_jm = 0;
59 
60 /* java.lang.Character */
61 jclass g_char_jc = 0;
62 jmethodID g_charinit_jm = 0;
63 jmethodID g_charval_jm = 0;
64 
65 /* java.lang.Short */
66 jclass g_short_jc = 0;
67 jmethodID g_shortinit_jm = 0;
68 
69 /* java.lang.Integer */
70 jclass g_int_jc = 0;
71 jmethodID g_intinit_jm = 0;
72 
73 /* java.lang.Long */
74 jclass g_long_jc = 0;
75 jmethodID g_longinit_jm = 0;
76 
77 /* java.math.BigInteger */
78 jclass g_bigint_jc = 0;
79 jmethodID g_bigint_val_jsm = 0;
80 jmethodID g_bigint_div_jm = 0;
81 jmethodID g_bigint_shl_jm = 0;
82 jmethodID g_bigint_or_jm = 0;
83 jmethodID g_bigint_setbit_jm = 0;
84 
85 /* java.lang.String */
86 jclass g_string_jc = 0;
87 jmethodID g_strinit_bytes_jm = 0;
88 jmethodID g_strbytes_jm = 0;
89 jmethodID g_trim_jm = 0;
90 
91 /* java.lang.StringBuilder */
92 jclass g_buf_jc = 0;
93 jmethodID g_bufinit_jm = 0;
94 jmethodID g_buf_append_char_jm = 0;
95 jmethodID g_buf_append_int_jm = 0;
96 jmethodID g_buf_append_long_jm = 0;
97 jmethodID g_buf_append_str_jm = 0;
98 jmethodID g_buf_append_obj_jm = 0;
99 jmethodID g_buflen_jm = 0;
100 jmethodID g_bufsetlen_jm = 0;
101 
102 /* java.lang.Object */
103 jclass g_object_jc = 0;
104 jmethodID g_tostring_jm = 0;
105 jmethodID g_equals_jm = 0;
106 
107 /* java.lang.Enum */
108 jclass g_enum_jc = 0;
109 jmethodID g_enumname_jm = 0;
110 
111 /* List */
112 jclass g_list_jc = 0;
113 jmethodID g_listclear_jm = 0;
114 jmethodID g_listadd_jm = 0;
115 jmethodID g_listget_jm = 0;
116 jmethodID g_listsize_jm = 0;
117 
118 /* Global list pools */
119 static uu_list_pool_t *g_pointer_pool = NULL;
120 static uu_list_pool_t *g_string_pool = NULL;
121 
122 static dtj_status_t dtj_get_jni_classes(JNIEnv *, uu_list_t *, uu_list_pool_t *,
123     uu_list_pool_t *, uu_list_pool_t *, const dtj_table_entry_t *);
124 static dtj_status_t dtj_cache_jni_methods(JNIEnv *, dtj_java_class_t *);
125 static dtj_status_t dtj_cache_jni_fields(JNIEnv *, dtj_java_class_t *);
126 
127 /* Constructors */
128 static dtj_java_class_t *dtj_java_class_create(JNIEnv *, jclass *, char *,
129     uu_list_pool_t *, uu_list_pool_t *, uu_list_pool_t *);
130 static dtj_java_method_t *dtj_java_method_create(JNIEnv *, jmethodID *, char *,
131     char *, uu_list_pool_t *);
132 static dtj_java_method_t *dtj_java_static_method_create(JNIEnv *, jmethodID *,
133     char *, char *, uu_list_pool_t *);
134 static dtj_java_field_t *dtj_java_field_create(JNIEnv *, jfieldID *, char *,
135     char *, uu_list_pool_t *);
136 static dtj_java_field_t *dtj_java_static_field_create(JNIEnv *, jfieldID *,
137     char *, char *, uu_list_pool_t *);
138 
139 /* Destructors */
140 static void dtj_java_class_destroy(void *, void *);
141 static void dtj_java_method_destroy(void *, void *);
142 static void dtj_java_field_destroy(void *, void *);
143 
144 /* Comparison functions, uu_compare_fn_t signature */
145 static int dtj_java_class_cmp(const void *, const void *, void *);
146 static int dtj_java_method_cmp(const void *, const void *, void *);
147 static int dtj_java_field_cmp(const void *, const void *, void *);
148 
149 /* Java Throwable */
150 static void dtj_throw(JNIEnv *, jclass, const char *, va_list *);
151 
152 /* Support for uu_list_t wrappers */
153 static boolean_t dtj_check_pointer_pool(void);
154 static boolean_t dtj_check_string_pool(void);
155 
156 dtj_status_t
dtj_load_common(JNIEnv * jenv)157 dtj_load_common(JNIEnv *jenv)
158 {
159 	dtj_status_t status;
160 
161 	static const dtj_table_entry_t table[] = {
162 		/* NativeException */
163 		{ JCLASS,  &g_nx_jc,
164 			"org/opensolaris/os/dtrace/NativeException" },
165 		{ JMETHOD, &g_nxinit_jm, CONSTRUCTOR,
166 			"(Ljava/lang/String;ILjava/lang/Throwable;)V" },
167 
168 		/* java.io.Serializable */
169 		{ JCLASS,  &g_serial_jc, "java/io/Serializable" },
170 
171 		/* java.lang.Number */
172 		{ JCLASS,  &g_number_jc, "java/lang/Number" },
173 		{ JMETHOD, &g_shortval_jm, "shortValue", "()S" },
174 		{ JMETHOD, &g_intval_jm, "intValue", "()I" },
175 		{ JMETHOD, &g_longval_jm, "longValue", "()J" },
176 
177 		/* java.lang.Byte */
178 		{ JCLASS,  &g_byte_jc, "java/lang/Byte" },
179 		{ JMETHOD, &g_byteinit_jm, CONSTRUCTOR, "(B)V" },
180 
181 		/* java.lang.Character */
182 		{ JCLASS,  &g_char_jc, "java/lang/Character" },
183 		{ JMETHOD, &g_charinit_jm, CONSTRUCTOR, "(C)V" },
184 		{ JMETHOD, &g_charval_jm, "charValue", "()C" },
185 
186 		/* java.lang.Short */
187 		{ JCLASS,  &g_short_jc, "java/lang/Short" },
188 		{ JMETHOD, &g_shortinit_jm, CONSTRUCTOR, "(S)V" },
189 
190 		/* java.lang.Integer */
191 		{ JCLASS,  &g_int_jc, "java/lang/Integer" },
192 		{ JMETHOD, &g_intinit_jm, CONSTRUCTOR, "(I)V" },
193 
194 		/* java.lang.Long */
195 		{ JCLASS,  &g_long_jc, "java/lang/Long" },
196 		{ JMETHOD, &g_longinit_jm, CONSTRUCTOR, "(J)V" },
197 
198 		/* java.math.BigInteger */
199 		{ JCLASS,  &g_bigint_jc, "java/math/BigInteger" },
200 		{ JMETHOD_STATIC, &g_bigint_val_jsm, "valueOf",
201 			"(J)Ljava/math/BigInteger;" },
202 		{ JMETHOD, &g_bigint_div_jm, "divide",
203 			"(Ljava/math/BigInteger;)Ljava/math/BigInteger;" },
204 		{ JMETHOD, &g_bigint_shl_jm, "shiftLeft",
205 			"(I)Ljava/math/BigInteger;" },
206 		{ JMETHOD, &g_bigint_or_jm, "or",
207 			"(Ljava/math/BigInteger;)Ljava/math/BigInteger;" },
208 		{ JMETHOD, &g_bigint_setbit_jm, "setBit",
209 			"(I)Ljava/math/BigInteger;" },
210 
211 		/* java.lang.String */
212 		{ JCLASS,  &g_string_jc, "java/lang/String" },
213 		{ JMETHOD, &g_strinit_bytes_jm, CONSTRUCTOR, "([B)V" },
214 		{ JMETHOD, &g_strbytes_jm, "getBytes", "()[B" },
215 		{ JMETHOD, &g_trim_jm, "trim", "()Ljava/lang/String;" },
216 
217 		/* java.lang.StringBuilder */
218 		{ JCLASS,  &g_buf_jc, "java/lang/StringBuilder" },
219 		{ JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" },
220 		{ JMETHOD, &g_buf_append_char_jm, "append",
221 			"(C)Ljava/lang/StringBuilder;" },
222 		{ JMETHOD, &g_buf_append_int_jm, "append",
223 			"(I)Ljava/lang/StringBuilder;" },
224 		{ JMETHOD, &g_buf_append_long_jm, "append",
225 			"(J)Ljava/lang/StringBuilder;" },
226 		{ JMETHOD, &g_buf_append_str_jm, "append",
227 			"(Ljava/lang/String;)Ljava/lang/StringBuilder;" },
228 		{ JMETHOD, &g_buf_append_obj_jm, "append",
229 			"(Ljava/lang/Object;)Ljava/lang/StringBuilder;" },
230 		{ JMETHOD, &g_buflen_jm, "length", "()I" },
231 		{ JMETHOD, &g_bufsetlen_jm, "setLength", "(I)V" },
232 
233 		/* java.lang.Object */
234 		{ JCLASS,  &g_object_jc, "java/lang/Object" },
235 		{ JMETHOD, &g_tostring_jm, "toString",
236 			"()Ljava/lang/String;" },
237 		{ JMETHOD, &g_equals_jm, "equals",
238 			"(Ljava/lang/Object;)Z" },
239 
240 		/* java.lang.Enum */
241 		{ JCLASS,  &g_enum_jc, "java/lang/Enum" },
242 		{ JMETHOD, &g_enumname_jm, "name",
243 			"()Ljava/lang/String;" },
244 
245 		/* List */
246 		{ JCLASS, &g_list_jc, "java/util/List" },
247 		{ JMETHOD, &g_listclear_jm, "clear", "()V" },
248 		{ JMETHOD, &g_listadd_jm, "add", "(Ljava/lang/Object;)Z" },
249 		{ JMETHOD, &g_listget_jm, "get", "(I)Ljava/lang/Object;" },
250 		{ JMETHOD, &g_listsize_jm, "size", "()I" },
251 
252 		{ DTJ_TYPE_END }
253 	};
254 
255 	status = dtj_cache_jni_classes(jenv, table);
256 	if (status == DTJ_OK) {
257 		g_dtj_load_common = B_TRUE;
258 	}
259 	return (status);
260 }
261 
262 static int
263 /* ARGSUSED */
dtj_java_class_cmp(const void * v1,const void * v2,void * arg)264 dtj_java_class_cmp(const void * v1, const void * v2, void *arg)
265 {
266 	const dtj_java_class_t *c1 = v1;
267 	const dtj_java_class_t *c2 = v2;
268 	return (strcmp(c1->djc_name, c2->djc_name));
269 }
270 
271 static int
272 /* ARGSUSED */
dtj_java_method_cmp(const void * v1,const void * v2,void * arg)273 dtj_java_method_cmp(const void *v1, const void *v2, void *arg)
274 {
275 	int cmp;
276 	const dtj_java_method_t *m1 = v1;
277 	const dtj_java_method_t *m2 = v2;
278 	cmp = strcmp(m1->djm_name, m2->djm_name);
279 	if (cmp == 0) {
280 		cmp = strcmp(m1->djm_signature, m2->djm_signature);
281 	}
282 	return (cmp);
283 }
284 
285 static int
286 /* ARGSUSED */
dtj_java_field_cmp(const void * v1,const void * v2,void * arg)287 dtj_java_field_cmp(const void *v1, const void *v2, void *arg)
288 {
289 	const dtj_java_field_t *f1 = v1;
290 	const dtj_java_field_t *f2 = v2;
291 	return (strcmp(f1->djf_name, f2->djf_name));
292 }
293 
294 static dtj_java_class_t *
dtj_java_class_create(JNIEnv * jenv,jclass * jc,char * name,uu_list_pool_t * classpool,uu_list_pool_t * methodpool,uu_list_pool_t * fieldpool)295 dtj_java_class_create(JNIEnv *jenv, jclass *jc, char *name,
296     uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
297     uu_list_pool_t *fieldpool)
298 {
299 	dtj_java_class_t *c = uu_zalloc(sizeof (dtj_java_class_t));
300 	if (c) {
301 		uu_list_node_init(c, &c->djc_node, classpool);
302 		c->djc_ptr = jc;
303 		c->djc_name = name;
304 		c->djc_methods = uu_list_create(methodpool, NULL,
305 		    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
306 		if (!c->djc_methods) {
307 			dtj_throw_out_of_memory(jenv,
308 			    "Failed method list creation");
309 			uu_list_node_fini(c, &c->djc_node, classpool);
310 			free(c);
311 			c = NULL;
312 		}
313 		c->djc_fields = uu_list_create(fieldpool, NULL,
314 		    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
315 		if (!c->djc_fields) {
316 			dtj_throw_out_of_memory(jenv,
317 			    "Failed field list creation");
318 			uu_list_destroy(c->djc_methods);
319 			c->djc_methods = NULL;
320 			uu_list_node_fini(c, &c->djc_node, classpool);
321 			free(c);
322 			c = NULL;
323 		}
324 	} else {
325 		dtj_throw_out_of_memory(jenv,
326 		    "Failed to allocate class description");
327 	}
328 	return (c);
329 }
330 
331 static dtj_java_method_t *
dtj_java_method_create(JNIEnv * jenv,jmethodID * jm,char * name,char * signature,uu_list_pool_t * methodpool)332 dtj_java_method_create(JNIEnv *jenv, jmethodID *jm, char *name, char *signature,
333     uu_list_pool_t *methodpool)
334 {
335 	dtj_java_method_t *m = uu_zalloc(sizeof (dtj_java_method_t));
336 	if (m) {
337 		uu_list_node_init(m, &m->djm_node, methodpool);
338 		m->djm_ptr = jm;
339 		m->djm_name = name;
340 		m->djm_signature = signature;
341 		m->djm_static = B_FALSE;
342 	} else {
343 		dtj_throw_out_of_memory(jenv,
344 		    "Failed to allocate method description");
345 	}
346 	return (m);
347 }
348 
349 static dtj_java_method_t *
dtj_java_static_method_create(JNIEnv * jenv,jmethodID * jm,char * name,char * signature,uu_list_pool_t * methodpool)350 dtj_java_static_method_create(JNIEnv *jenv, jmethodID *jm, char *name,
351     char *signature, uu_list_pool_t *methodpool)
352 {
353 	dtj_java_method_t *m = dtj_java_method_create(jenv, jm, name, signature,
354 	    methodpool);
355 	if (m) {
356 		m->djm_static = B_TRUE;
357 	}
358 	return (m);
359 }
360 
361 static dtj_java_field_t *
dtj_java_field_create(JNIEnv * jenv,jfieldID * jf,char * name,char * type,uu_list_pool_t * fieldpool)362 dtj_java_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
363     uu_list_pool_t *fieldpool)
364 {
365 	dtj_java_field_t *f = uu_zalloc(sizeof (dtj_java_field_t));
366 	if (f) {
367 		uu_list_node_init(f, &f->djf_node, fieldpool);
368 		f->djf_ptr = jf;
369 		f->djf_name = name;
370 		f->djf_type = type;
371 		f->djf_static = B_FALSE;
372 	} else {
373 		dtj_throw_out_of_memory(jenv,
374 		    "Failed to allocate field description");
375 	}
376 	return (f);
377 }
378 
379 static dtj_java_field_t *
dtj_java_static_field_create(JNIEnv * jenv,jfieldID * jf,char * name,char * type,uu_list_pool_t * fieldpool)380 dtj_java_static_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
381     uu_list_pool_t *fieldpool)
382 {
383 	dtj_java_field_t *f = dtj_java_field_create(jenv, jf, name, type,
384 	    fieldpool);
385 	if (f) {
386 		f->djf_static = B_TRUE;
387 	}
388 	return (f);
389 }
390 
391 static void
392 /* ARGSUSED */
dtj_java_class_destroy(void * v,void * arg)393 dtj_java_class_destroy(void *v, void *arg)
394 {
395 	if (v) {
396 		dtj_java_class_t *c = v;
397 		c->djc_ptr = NULL;  /* do not free user-defined storage */
398 		c->djc_name = NULL; /* string literal */
399 		dtj_list_destroy(c->djc_methods, dtj_java_method_destroy, NULL);
400 		dtj_list_destroy(c->djc_fields, dtj_java_field_destroy, NULL);
401 		c->djc_methods = NULL;
402 		c->djc_fields = NULL;
403 		uu_free(v);
404 	}
405 }
406 
407 static void
408 /* ARGSUSED */
dtj_java_method_destroy(void * v,void * arg)409 dtj_java_method_destroy(void *v, void *arg)
410 {
411 	if (v) {
412 		dtj_java_method_t *m = v;
413 		m->djm_ptr = NULL;	/* do not free user-defined space */
414 		m->djm_name = NULL;	/* string literal */
415 		m->djm_signature = NULL;	/* string literal */
416 		uu_free(v);
417 	}
418 }
419 
420 static void
421 /* ARGSUSED */
dtj_java_field_destroy(void * v,void * arg)422 dtj_java_field_destroy(void *v, void *arg)
423 {
424 	if (v) {
425 		dtj_java_field_t *f = v;
426 		f->djf_ptr = NULL;  /* do not free user-defined space */
427 		f->djf_name = NULL; /* string literal */
428 		f->djf_type = NULL; /* string literal */
429 		uu_free(f);
430 	}
431 }
432 
433 dtj_status_t
dtj_cache_jni_classes(JNIEnv * jenv,const dtj_table_entry_t * table)434 dtj_cache_jni_classes(JNIEnv *jenv, const dtj_table_entry_t *table)
435 {
436 	dtj_java_class_t *class;
437 	uu_list_pool_t *classpool;
438 	uu_list_pool_t *methodpool;
439 	uu_list_pool_t *fieldpool;
440 	uu_list_t *classes;
441 	uu_list_walk_t *itr;
442 	jclass jc;
443 	jclass gjc;
444 	dtj_status_t status;
445 
446 	classpool = uu_list_pool_create("classpool",
447 	    sizeof (dtj_java_class_t),
448 	    offsetof(dtj_java_class_t, djc_node), dtj_java_class_cmp,
449 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
450 	if (!classpool) {
451 		dtj_throw_out_of_memory(jenv, "failed class pool creation");
452 		return (DTJ_ERR);
453 	}
454 	methodpool = uu_list_pool_create("methodpool",
455 	    sizeof (dtj_java_method_t),
456 	    offsetof(dtj_java_method_t, djm_node), dtj_java_method_cmp,
457 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
458 	if (!methodpool) {
459 		dtj_throw_out_of_memory(jenv, "failed method pool creation");
460 		return (DTJ_ERR);
461 	}
462 	fieldpool = uu_list_pool_create("fieldpool",
463 	    sizeof (dtj_java_field_t),
464 	    offsetof(dtj_java_field_t, djf_node), dtj_java_field_cmp,
465 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
466 	if (!fieldpool) {
467 		dtj_throw_out_of_memory(jenv, "failed field pool creation");
468 		return (DTJ_ERR);
469 	}
470 
471 	classes = uu_list_create(classpool, NULL,
472 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
473 	if (!classes) {
474 		dtj_throw_out_of_memory(jenv, "failed class list creation");
475 		return (DTJ_ERR);
476 	}
477 
478 	status = dtj_get_jni_classes(jenv, classes, classpool, methodpool,
479 	    fieldpool, table);
480 	if (status != DTJ_OK) {
481 		/* java error pending */
482 		return (status);
483 	}
484 
485 	itr = uu_list_walk_start(classes, 0);
486 	while ((class = uu_list_walk_next(itr)) != NULL) {
487 		jc = (*jenv)->FindClass(jenv, class->djc_name);
488 		if (!jc) {
489 			/* NoClassDefFoundError pending */
490 			return (DTJ_ERR);
491 		}
492 		gjc = (*jenv)->NewGlobalRef(jenv, jc);
493 		(*jenv)->DeleteLocalRef(jenv, jc);
494 		if (!gjc) {
495 			dtj_throw_out_of_memory(jenv,
496 			    "Failed to create global class reference");
497 			return (DTJ_ERR);
498 		}
499 		*(class->djc_ptr) = gjc;
500 		status = dtj_cache_jni_methods(jenv, class);
501 		if (status != DTJ_OK) {
502 			/* java error pending */
503 			return (status);
504 		}
505 		status = dtj_cache_jni_fields(jenv, class);
506 		if (status != DTJ_OK) {
507 			/* java error pending */
508 			return (status);
509 		}
510 	}
511 	uu_list_walk_end(itr);
512 	dtj_list_destroy(classes, dtj_java_class_destroy, NULL);
513 	uu_list_pool_destroy(classpool);
514 	uu_list_pool_destroy(methodpool);
515 	uu_list_pool_destroy(fieldpool);
516 	return (DTJ_OK);
517 }
518 
519 /*
520  * Converts JNI table entry desriptions into java_class_t descriptors.
521  */
522 static dtj_status_t
dtj_get_jni_classes(JNIEnv * jenv,uu_list_t * classes,uu_list_pool_t * classpool,uu_list_pool_t * methodpool,uu_list_pool_t * fieldpool,const dtj_table_entry_t * table)523 dtj_get_jni_classes(JNIEnv *jenv, uu_list_t *classes,
524     uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
525     uu_list_pool_t *fieldpool, const dtj_table_entry_t *table)
526 {
527 	int i;
528 	dtj_java_class_t *c = NULL;
529 	dtj_java_method_t *m;
530 	dtj_java_field_t *f;
531 
532 	for (i = 0; table[i].djte_type != DTJ_TYPE_END; ++i) {
533 		/*
534 		 * Class not added until all of its method and field information
535 		 * is attached, so we defer adding a class until the next
536 		 * element with type JCLASS.
537 		 */
538 		switch (table[i].djte_type) {
539 		case JCLASS:
540 			if (c) {
541 				/* previous class */
542 				if (!dtj_list_add(classes, c)) {
543 					dtj_throw_out_of_memory(jenv,
544 					    "Failed to add class description");
545 					/*
546 					 * In response to an error return value,
547 					 * the caller will delete the class
548 					 * descriptions list with any
549 					 * descriptions created so far.
550 					 */
551 					return (DTJ_ERR);
552 				}
553 			}
554 			c = dtj_java_class_create(jenv,
555 			    (jclass *)table[i].djte_addr, table[i].djte_name,
556 			    classpool, methodpool, fieldpool);
557 			if (!c) {
558 				/* OutOfMemoryError pending */
559 				return (DTJ_ERR);
560 			}
561 			break;
562 		case JMETHOD:
563 			if (!c) {
564 				dtj_throw_illegal_state(jenv,
565 				    "method description not preceded "
566 				    "by class description");
567 				return (DTJ_ERR);
568 			}
569 			m = dtj_java_method_create(jenv,
570 			    (jmethodID *)table[i].djte_addr,
571 			    table[i].djte_name, table[i].djte_desc,
572 			    methodpool);
573 			if (!m) {
574 				/* OutOfMemoryError pending */
575 				return (DTJ_ERR);
576 			}
577 			if (!dtj_list_add(c->djc_methods, m)) {
578 				dtj_throw_out_of_memory(jenv,
579 				    "Failed to add method description");
580 				return (DTJ_ERR);
581 			}
582 			break;
583 		case JMETHOD_STATIC:
584 			if (!c) {
585 				dtj_throw_illegal_state(jenv,
586 				    "static method description not preceded "
587 				    "by class description");
588 				return (DTJ_ERR);
589 			}
590 			m = dtj_java_static_method_create(jenv,
591 			    (jmethodID *)table[i].djte_addr,
592 			    table[i].djte_name, table[i].djte_desc,
593 			    methodpool);
594 			if (!m) {
595 				/* OutOfMemoryError pending */
596 				return (DTJ_ERR);
597 			}
598 			if (!dtj_list_add(c->djc_methods, m)) {
599 				dtj_throw_out_of_memory(jenv,
600 				    "Failed to add static method description");
601 				return (DTJ_ERR);
602 			}
603 			break;
604 		case JFIELD:
605 			if (!c) {
606 				dtj_throw_illegal_state(jenv,
607 				    "field description not preceded "
608 				    "by class description");
609 				return (DTJ_ERR);
610 			}
611 			f = dtj_java_field_create(jenv,
612 			    (jfieldID *)table[i].djte_addr,
613 			    table[i].djte_name, table[i].djte_desc,
614 			    fieldpool);
615 			if (!f) {
616 				/* OutOfMemoryError pending */
617 				return (DTJ_ERR);
618 			}
619 			if (!dtj_list_add(c->djc_fields, f)) {
620 				dtj_throw_out_of_memory(jenv,
621 				    "Failed to add field description");
622 				return (DTJ_ERR);
623 			}
624 			break;
625 		case JFIELD_STATIC:
626 			if (!c) {
627 				dtj_throw_illegal_state(jenv,
628 				    "static field description not preceded "
629 				    "by class description");
630 				return (DTJ_ERR);
631 			}
632 			f = dtj_java_static_field_create(jenv,
633 			    (jfieldID *)table[i].djte_addr,
634 			    table[i].djte_name, table[i].djte_desc,
635 			    fieldpool);
636 			if (!f) {
637 				/* OutOfMemoryError pending */
638 				return (DTJ_ERR);
639 			}
640 			if (!dtj_list_add(c->djc_fields, f)) {
641 				dtj_throw_out_of_memory(jenv,
642 				    "Failed to add static field description");
643 				return (DTJ_ERR);
644 			}
645 			break;
646 		default:
647 			dtj_throw_illegal_state(jenv,
648 			    "Unexpected jni_type_e: %d", table[i].djte_type);
649 			return (DTJ_ERR);
650 		}
651 	}
652 	if (c) {
653 		/* last class */
654 		if (!dtj_list_add(classes, c)) {
655 			dtj_throw_out_of_memory(jenv,
656 			    "Failed to add class description");
657 			return (DTJ_ERR);
658 		}
659 	}
660 
661 	return (DTJ_OK);
662 }
663 
664 static dtj_status_t
dtj_cache_jni_methods(JNIEnv * jenv,dtj_java_class_t * c)665 dtj_cache_jni_methods(JNIEnv *jenv, dtj_java_class_t *c)
666 {
667 	dtj_java_method_t *method;
668 	jmethodID jm;
669 	uu_list_walk_t *itr;
670 	itr = uu_list_walk_start(c->djc_methods, 0);
671 	while ((method = uu_list_walk_next(itr)) != NULL) {
672 		if (method->djm_static) {
673 			jm = (*jenv)->GetStaticMethodID(jenv, *(c->djc_ptr),
674 			    method->djm_name, method->djm_signature);
675 		} else {
676 			jm = (*jenv)->GetMethodID(jenv, *(c->djc_ptr),
677 			    method->djm_name, method->djm_signature);
678 		}
679 		if (jm == 0) {
680 			/*
681 			 * The pending NoSuchMethodError gives only the
682 			 * method name, which is not so helpful for
683 			 * overloaded methods and methods such as <init>
684 			 * that have the same name in multiple classes.
685 			 * Clear the pending error and throw one that
686 			 * includes the class name and the method
687 			 * signature.
688 			 */
689 			jclass jc;
690 			char msg[DTJ_MSG_SIZE];
691 			(*jenv)->ExceptionClear(jenv);
692 			(void) snprintf(msg, sizeof (msg), "%s %s %s",
693 			    c->djc_name, method->djm_name,
694 			    method->djm_signature);
695 
696 			jc = (*jenv)->FindClass(jenv,
697 			    "java/lang/NoSuchMethodError");
698 			(*jenv)->ThrowNew(jenv, jc, msg);
699 			(*jenv)->DeleteLocalRef(jenv, jc);
700 			return (DTJ_ERR);
701 		}
702 		*(method->djm_ptr) = jm;
703 	}
704 	uu_list_walk_end(itr);
705 	return (DTJ_OK);
706 }
707 
708 static dtj_status_t
dtj_cache_jni_fields(JNIEnv * jenv,dtj_java_class_t * c)709 dtj_cache_jni_fields(JNIEnv *jenv, dtj_java_class_t *c)
710 {
711 	dtj_java_field_t *field;
712 	jfieldID jf;
713 	uu_list_walk_t *itr;
714 	itr = uu_list_walk_start(c->djc_fields, 0);
715 	while ((field = uu_list_walk_next(itr)) != NULL) {
716 		if (field->djf_static) {
717 			jf = (*jenv)->GetStaticFieldID(jenv, *(c->djc_ptr),
718 			    field->djf_name, field->djf_type);
719 		} else {
720 			jf = (*jenv)->GetFieldID(jenv, *(c->djc_ptr),
721 			    field->djf_name, field->djf_type);
722 		}
723 		if (jf == 0) {
724 			jclass jc;
725 			char msg[DTJ_MSG_SIZE];
726 			(*jenv)->ExceptionClear(jenv);
727 			(void) snprintf(msg, sizeof (msg),
728 			    "%s.%s signature: %s", c->djc_name,
729 			    field->djf_name, field->djf_type);
730 
731 			jc = (*jenv)->FindClass(jenv,
732 			    "java/lang/NoSuchFieldError");
733 			(*jenv)->ThrowNew(jenv, jc, msg);
734 			(*jenv)->DeleteLocalRef(jenv, jc);
735 			return (DTJ_ERR);
736 		}
737 		*(field->djf_ptr) = jf;
738 	}
739 	uu_list_walk_end(itr);
740 	return (DTJ_OK);
741 }
742 
743 
744 /* Common utilities */
745 
746 static void
dtj_throw(JNIEnv * jenv,jclass jc,const char * fmt,va_list * ap)747 dtj_throw(JNIEnv *jenv, jclass jc, const char *fmt, va_list *ap)
748 {
749 	char msg[DTJ_MSG_SIZE];
750 	(void) vsnprintf(msg, sizeof (msg), fmt, *ap);
751 	(*jenv)->ThrowNew(jenv, jc, msg);
752 }
753 
754 void
dtj_throw_out_of_memory(JNIEnv * jenv,const char * fmt,...)755 dtj_throw_out_of_memory(JNIEnv *jenv, const char *fmt, ...)
756 {
757 	va_list ap;
758 	jclass jc;
759 	/*
760 	 * JNI documentation unclear whether NewGlobalRef() can throw
761 	 * OutOfMemoryError, so we'll make this function safe in case
762 	 * OutOfMemoryError has already been thrown
763 	 */
764 	if ((*jenv)->ExceptionCheck(jenv)) {
765 		return;
766 	}
767 	jc = (*jenv)->FindClass(jenv,
768 	    "java/lang/OutOfMemoryError");
769 	va_start(ap, fmt);
770 	dtj_throw(jenv, jc, fmt, &ap);
771 	(*jenv)->DeleteLocalRef(jenv, jc);
772 	va_end(ap);
773 }
774 
775 void
dtj_throw_null_pointer(JNIEnv * jenv,const char * fmt,...)776 dtj_throw_null_pointer(JNIEnv *jenv, const char *fmt, ...)
777 {
778 	va_list ap;
779 	jclass jc = (*jenv)->FindClass(jenv,
780 	    "java/lang/NullPointerException");
781 	va_start(ap, fmt);
782 	dtj_throw(jenv, jc, fmt, &ap);
783 	(*jenv)->DeleteLocalRef(jenv, jc);
784 	va_end(ap);
785 }
786 
787 void
dtj_throw_illegal_state(JNIEnv * jenv,const char * fmt,...)788 dtj_throw_illegal_state(JNIEnv *jenv, const char *fmt, ...)
789 {
790 	va_list ap;
791 	jclass jc = (*jenv)->FindClass(jenv,
792 	    "java/lang/IllegalStateException");
793 	va_start(ap, fmt);
794 	dtj_throw(jenv, jc, fmt, &ap);
795 	(*jenv)->DeleteLocalRef(jenv, jc);
796 	va_end(ap);
797 }
798 
799 void
dtj_throw_illegal_argument(JNIEnv * jenv,const char * fmt,...)800 dtj_throw_illegal_argument(JNIEnv *jenv, const char *fmt, ...)
801 {
802 	va_list ap;
803 	jclass jc = (*jenv)->FindClass(jenv,
804 	    "java/lang/IllegalArgumentException");
805 	va_start(ap, fmt);
806 	dtj_throw(jenv, jc, fmt, &ap);
807 	(*jenv)->DeleteLocalRef(jenv, jc);
808 	va_end(ap);
809 }
810 
811 void
dtj_throw_no_such_element(JNIEnv * jenv,const char * fmt,...)812 dtj_throw_no_such_element(JNIEnv *jenv, const char *fmt, ...)
813 {
814 	va_list ap;
815 	jclass jc = (*jenv)->FindClass(jenv,
816 	    "java/util/NoSuchElementException");
817 	va_start(ap, fmt);
818 	dtj_throw(jenv, jc, fmt, &ap);
819 	(*jenv)->DeleteLocalRef(jenv, jc);
820 	va_end(ap);
821 }
822 
823 void
dtj_throw_class_cast(JNIEnv * jenv,const char * fmt,...)824 dtj_throw_class_cast(JNIEnv *jenv, const char *fmt, ...)
825 {
826 	va_list ap;
827 	jclass jc = (*jenv)->FindClass(jenv,
828 	    "java/lang/ClassCastException");
829 	va_start(ap, fmt);
830 	dtj_throw(jenv, jc, fmt, &ap);
831 	(*jenv)->DeleteLocalRef(jenv, jc);
832 	va_end(ap);
833 }
834 
835 void
dtj_throw_assertion(JNIEnv * jenv,const char * fmt,...)836 dtj_throw_assertion(JNIEnv *jenv, const char *fmt, ...)
837 {
838 	va_list ap;
839 	jclass jc = (*jenv)->FindClass(jenv,
840 	    "java/lang/AssertionError");
841 	va_start(ap, fmt);
842 	dtj_throw(jenv, jc, fmt, &ap);
843 	(*jenv)->DeleteLocalRef(jenv, jc);
844 	va_end(ap);
845 }
846 
847 void
dtj_throw_resource_limit(JNIEnv * jenv,const char * fmt,...)848 dtj_throw_resource_limit(JNIEnv *jenv, const char *fmt, ...)
849 {
850 	va_list ap;
851 	jclass jc = (*jenv)->FindClass(jenv,
852 	    "org/opensolaris/os/dtrace/ResourceLimitException");
853 	va_start(ap, fmt);
854 	dtj_throw(jenv, jc, fmt, &ap);
855 	(*jenv)->DeleteLocalRef(jenv, jc);
856 	va_end(ap);
857 }
858 
859 void
dtj_wrap_exception(JNIEnv * jenv,const char * file,int line)860 dtj_wrap_exception(JNIEnv *jenv, const char *file, int line)
861 {
862 	jthrowable e = NULL;
863 	jthrowable nx = NULL;
864 	jstring jfile = NULL;
865 
866 	e = (*jenv)->ExceptionOccurred(jenv);
867 	if (!e) {
868 		return;
869 	}
870 
871 	if (!g_dtj_load_common) {
872 		return;
873 	}
874 
875 	(*jenv)->ExceptionClear(jenv);
876 
877 	/* Unsafe to test while exception pending */
878 	if ((*jenv)->IsInstanceOf(jenv, e, g_nx_jc)) {
879 		/* Already wrapped */
880 		(*jenv)->Throw(jenv, e);
881 		(*jenv)->DeleteLocalRef(jenv, e);
882 		return;
883 	}
884 
885 	jfile = dtj_NewStringNative(jenv, file);
886 	if ((*jenv)->ExceptionCheck(jenv)) {
887 		/*
888 		 * Only wrap the exception if possible, otherwise just throw the
889 		 * original exception.
890 		 */
891 		(*jenv)->ExceptionClear(jenv);
892 		(*jenv)->Throw(jenv, e);
893 		(*jenv)->DeleteLocalRef(jenv, e);
894 		return;
895 	}
896 
897 	nx = (jthrowable)(*jenv)->NewObject(jenv, g_nx_jc, g_nxinit_jm,
898 	    jfile, line, e);
899 	(*jenv)->DeleteLocalRef(jenv, jfile);
900 	if ((*jenv)->ExceptionCheck(jenv)) {
901 		(*jenv)->ExceptionClear(jenv);
902 		(*jenv)->Throw(jenv, e);
903 		(*jenv)->DeleteLocalRef(jenv, e);
904 		return;
905 	}
906 
907 	(*jenv)->DeleteLocalRef(jenv, e);
908 	(*jenv)->Throw(jenv, nx);
909 	(*jenv)->DeleteLocalRef(jenv, nx);
910 }
911 
912 /*
913  * Calls the given java object's toString() method and prints the value to
914  * stdout.  Useful for debugging.  Guaranteed that no exception is pending when
915  * this function returns.
916  */
917 void
dtj_print_object(JNIEnv * jenv,jobject jobj)918 dtj_print_object(JNIEnv *jenv, jobject jobj)
919 {
920 	jstring jstr;
921 	const char *cstr;
922 
923 	if (!g_dtj_load_common) {
924 		dtj_throw_illegal_state(jenv,
925 		    "dtj_load_common() has not been called");
926 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
927 		return;
928 	}
929 
930 	if (!jobj) {
931 		(void) printf("null\n");
932 		return;
933 	}
934 
935 	jstr = (*jenv)->CallObjectMethod(jenv, jobj, g_tostring_jm);
936 	if ((*jenv)->ExceptionCheck(jenv)) {
937 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
938 		return;
939 	}
940 	cstr = (*jenv)->GetStringUTFChars(jenv, jstr, 0);
941 	if (cstr) {
942 		(void) printf("%s\n", cstr);
943 	} else {
944 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
945 		(*jenv)->DeleteLocalRef(jenv, jstr);
946 		return;
947 	}
948 	(*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr);
949 	(*jenv)->DeleteLocalRef(jenv, jstr);
950 }
951 
952 jobject
dtj_uint64(JNIEnv * jenv,uint64_t u)953 dtj_uint64(JNIEnv *jenv, uint64_t u)
954 {
955 	int64_t i = (int64_t)u;
956 	jobject val64;
957 
958 	if (i >= 0) {
959 		val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
960 		    g_bigint_val_jsm, u);
961 	} else {
962 		jobject tmp;
963 
964 		u ^= ((uint64_t)0x1 << 63);
965 		val64 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
966 		    g_bigint_val_jsm, u);
967 		tmp = val64;
968 		val64 = (*jenv)->CallObjectMethod(jenv, tmp,
969 		    g_bigint_setbit_jm, 63);
970 		(*jenv)->DeleteLocalRef(jenv, tmp);
971 	}
972 
973 	return (val64);
974 }
975 
976 jobject
dtj_int128(JNIEnv * jenv,uint64_t high,uint64_t low)977 dtj_int128(JNIEnv *jenv, uint64_t high, uint64_t low)
978 {
979 	jobject val128;
980 	jobject low64;
981 	jobject tmp;
982 
983 	val128 = (*jenv)->CallStaticObjectMethod(jenv, g_bigint_jc,
984 	    g_bigint_val_jsm, high);
985 	tmp = val128;
986 	val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_shl_jm, 64);
987 	(*jenv)->DeleteLocalRef(jenv, tmp);
988 	low64 = dtj_uint64(jenv, low);
989 	tmp = val128;
990 	val128 = (*jenv)->CallObjectMethod(jenv, tmp, g_bigint_or_jm, low64);
991 	(*jenv)->DeleteLocalRef(jenv, tmp);
992 	(*jenv)->DeleteLocalRef(jenv, low64);
993 
994 	return (val128);
995 }
996 
997 jstring
dtj_format_string(JNIEnv * jenv,const char * fmt,...)998 dtj_format_string(JNIEnv *jenv, const char *fmt, ...)
999 {
1000 	va_list ap;
1001 	char str[DTJ_MSG_SIZE];
1002 
1003 	jstring jstr = NULL;
1004 
1005 	va_start(ap, fmt);
1006 	(void) vsnprintf(str, sizeof (str), fmt, ap);
1007 	va_end(ap);
1008 
1009 	jstr = dtj_NewStringNative(jenv, str);
1010 	/* return NULL if OutOfMemoryError pending */
1011 	return (jstr);
1012 }
1013 
1014 jstring
dtj_NewStringNative(JNIEnv * jenv,const char * str)1015 dtj_NewStringNative(JNIEnv *jenv, const char *str)
1016 {
1017 	jstring result;
1018 	jbyteArray bytes = 0;
1019 	int len;
1020 
1021 	if (!g_dtj_load_common) {
1022 		dtj_throw_illegal_state(jenv,
1023 		    "dtj_load_common() has not been called");
1024 		return (NULL);
1025 	}
1026 
1027 	len = strlen(str);
1028 
1029 	bytes = (*jenv)->NewByteArray(jenv, len);
1030 	if (!bytes) {
1031 		return (NULL); /* OutOfMemoryError pending */
1032 	}
1033 	(*jenv)->SetByteArrayRegion(jenv, bytes, 0, len,
1034 	    (jbyte *)str);
1035 	if ((*jenv)->ExceptionCheck(jenv)) {
1036 		(*jenv)->DeleteLocalRef(jenv, bytes);
1037 		return (NULL); /* ArrayIndexOutOfBoundsException pending */
1038 	}
1039 	result = (*jenv)->NewObject(jenv, g_string_jc, g_strinit_bytes_jm,
1040 	    bytes);
1041 	(*jenv)->DeleteLocalRef(jenv, bytes);
1042 	/* return NULL result if exception pending */
1043 	return (result);
1044 }
1045 
1046 char *
dtj_GetStringNativeChars(JNIEnv * jenv,jstring jstr)1047 dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr)
1048 {
1049 	jbyteArray bytes = NULL;
1050 
1051 	jint len;
1052 	char *result = NULL;
1053 
1054 	if (!g_dtj_load_common) {
1055 		dtj_throw_illegal_state(jenv,
1056 		    "dtj_load_common() has not been called");
1057 		return (NULL);
1058 	}
1059 
1060 	bytes = (*jenv)->CallObjectMethod(jenv, jstr, g_strbytes_jm);
1061 	if ((*jenv)->ExceptionCheck(jenv)) {
1062 		return (NULL); /* OutOfMemoryError pending */
1063 	}
1064 	/* Does not throw exceptions */
1065 	len = (*jenv)->GetArrayLength(jenv, bytes);
1066 	result = malloc(len + 1);
1067 	if (!result) {
1068 		(*jenv)->DeleteLocalRef(jenv, bytes);
1069 		dtj_throw_out_of_memory(jenv,
1070 		    "could not allocate native chars");
1071 		return (NULL);
1072 	}
1073 
1074 	/* Skip check for ArrayIndexOutOfBoundsException */
1075 	(*jenv)->GetByteArrayRegion(jenv, bytes, 0, len,
1076 	    (jbyte *)result);
1077 	(*jenv)->DeleteLocalRef(jenv, bytes);
1078 	result[len] = '\0'; /* NUL-terminate */
1079 
1080 	return (result);
1081 }
1082 
1083 void
1084 /* ARGSUSED */
dtj_ReleaseStringNativeChars(JNIEnv * jenv,jstring jstr,const char * str)1085 dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, const char *str)
1086 {
1087 	free((void *)str);
1088 }
1089 
1090 char **
dtj_get_argv(JNIEnv * jenv,jobjectArray args,int * argc)1091 dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc)
1092 {
1093 	char **argv = NULL; /* return value */
1094 	const char *str;
1095 	int i;
1096 
1097 	jstring jstr = NULL;
1098 
1099 	if (!g_dtj_load_common) {
1100 		dtj_throw_illegal_state(jenv,
1101 		    "dtj_load_common() has not been called");
1102 		return (NULL);
1103 	}
1104 
1105 	*argc = (*jenv)->GetArrayLength(jenv, args);
1106 	/*
1107 	 * Initialize all string pointers to NULL so that in case of an error
1108 	 * filling in the array, free_argv() will not attempt to free the
1109 	 * unallocated elements.  Also NULL-terminate the string array for
1110 	 * functions that expect terminating NULL rather than rely on argc.
1111 	 */
1112 	argv = uu_zalloc((sizeof (char *)) * (*argc + 1));
1113 	if (!argv) {
1114 		dtj_throw_out_of_memory(jenv, "Failed to allocate args array");
1115 		return (NULL);
1116 	}
1117 
1118 	for (i = 0; i < *argc; ++i) {
1119 		jstr = (*jenv)->GetObjectArrayElement(jenv, args, i);
1120 		if ((*jenv)->ExceptionCheck(jenv)) {
1121 			dtj_free_argv(argv);
1122 			return (NULL);
1123 		}
1124 		str = dtj_GetStringNativeChars(jenv, jstr);
1125 		if ((*jenv)->ExceptionCheck(jenv)) {
1126 			dtj_free_argv(argv);
1127 			(*jenv)->DeleteLocalRef(jenv, jstr);
1128 			return (NULL);
1129 		}
1130 		argv[i] = malloc(strlen(str) + 1);
1131 		if (!argv[i]) {
1132 			dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
1133 			dtj_free_argv(argv);
1134 			dtj_ReleaseStringNativeChars(jenv, jstr, str);
1135 			(*jenv)->DeleteLocalRef(jenv, jstr);
1136 			return (NULL);
1137 		}
1138 		(void) strcpy(argv[i], str);
1139 		dtj_ReleaseStringNativeChars(jenv, jstr, str);
1140 		(*jenv)->DeleteLocalRef(jenv, jstr);
1141 		jstr = NULL;
1142 	}
1143 
1144 	return (argv);
1145 }
1146 
1147 char **
dtj_make_argv(JNIEnv * jenv,jstring command,int * argc)1148 dtj_make_argv(JNIEnv *jenv, jstring command, int *argc)
1149 {
1150 	const char *ws = "\f\n\r\t\v ";
1151 	char **argv = NULL; /* return value */
1152 	const char *cmd; /* native command string */
1153 	char *s; /* writable command */
1154 	char *tok; /* token */
1155 	int len;
1156 
1157 	if (!g_dtj_load_common) {
1158 		dtj_throw_illegal_state(jenv,
1159 		    "dtj_load_common() has not been called");
1160 		return (NULL);
1161 	}
1162 
1163 	if (!command) {
1164 		dtj_throw_null_pointer(jenv, "command is null");
1165 		return (NULL);
1166 	} else if ((*jenv)->GetStringLength(jenv, command) == 0) {
1167 		dtj_throw_illegal_argument(jenv, "command is empty");
1168 		return (NULL);
1169 	}
1170 
1171 	cmd = dtj_GetStringNativeChars(jenv, command);
1172 	if ((*jenv)->ExceptionCheck(jenv)) {
1173 		return (NULL);
1174 	}
1175 	len = strlen(cmd);
1176 	s = malloc(len + 1);
1177 	if (!s) {
1178 		dtj_throw_out_of_memory(jenv,
1179 		    "failed to allocate command string");
1180 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1181 		return (NULL);
1182 	}
1183 	(void) strcpy(s, cmd);
1184 	/*
1185 	 * Initialize all string pointers to NULL so that in case of an error
1186 	 * filling in the array, free_argv() will not attempt to free the
1187 	 * unallocated elements.  Also NULL-terminate the string array for
1188 	 * functions that expect terminating NULL rather than rely on argc.
1189 	 * Allow for maximum length resulting from single-character tokens
1190 	 * separated by single spaces.
1191 	 */
1192 	argv = uu_zalloc(sizeof (char *) * (len / 2 + 1));
1193 	if (!argv) {
1194 		dtj_throw_out_of_memory(jenv, "failed to allocate args array");
1195 		free(s);
1196 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1197 		return (NULL);
1198 	}
1199 
1200 	*argc = 0;
1201 	for (tok = strtok(s, ws); tok != NULL; tok = strtok(NULL, ws)) {
1202 		argv[*argc] = malloc(strlen(tok) + 1);
1203 		if (!argv[*argc]) {
1204 			dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
1205 			dtj_free_argv(argv);
1206 			free(s);
1207 			dtj_ReleaseStringNativeChars(jenv, command, cmd);
1208 			return (NULL);
1209 		}
1210 		(void) strcpy(argv[(*argc)++], tok);
1211 	}
1212 
1213 	if (*argc == 0) {
1214 		dtj_throw_illegal_argument(jenv, "command is blank");
1215 		dtj_free_argv(argv);
1216 		free(s);
1217 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1218 		return (NULL);
1219 	}
1220 
1221 	free(s);
1222 	dtj_ReleaseStringNativeChars(jenv, command, cmd);
1223 	return (argv);
1224 }
1225 
1226 void
dtj_free_argv(char ** argv)1227 dtj_free_argv(char **argv)
1228 {
1229 	if (argv) {
1230 		char **s = argv;
1231 		while (*s) {
1232 			free((void *)*s);
1233 			*s++ = NULL;
1234 		}
1235 		free((void *)argv);
1236 	}
1237 }
1238 
1239 
1240 /* Wrappers for uu_list_t */
1241 
1242 int
1243 /* ARGSUSED */
dtj_pointer_list_entry_cmp(const void * v1,const void * v2,void * arg)1244 dtj_pointer_list_entry_cmp(const void *v1, const void *v2, void *arg)
1245 {
1246 	const dtj_pointer_list_entry_t *p1 = v1;
1247 	const dtj_pointer_list_entry_t *p2 = v2;
1248 
1249 	/*
1250 	 * It is not valid to compare pointers using the relational operators
1251 	 * unless they point to elements in the same array.
1252 	 */
1253 	uint64_t x = (uintptr_t)p1->dple_ptr;
1254 	uint64_t y = (uintptr_t)p2->dple_ptr;
1255 	int rc;
1256 	rc = ((x > y) ? 1 : ((x < y) ? -1 : 0));
1257 	return (rc);
1258 }
1259 
1260 int
1261 /* ARGSUSED */
dtj_string_list_entry_cmp(const void * v1,const void * v2,void * arg)1262 dtj_string_list_entry_cmp(const void *v1, const void *v2, void *arg)
1263 {
1264 	const dtj_string_list_entry_t *p1 = v1;
1265 	const dtj_string_list_entry_t *p2 = v2;
1266 	const char *s1 = p1->dsle_value;
1267 	const char *s2 = p2->dsle_value;
1268 	if (s1 == NULL) {
1269 		return (s2 == NULL ? 0 : -1);
1270 	}
1271 	if (s2 == NULL) {
1272 		return (1);
1273 	}
1274 	return (strcmp(s1, s2));
1275 }
1276 
1277 static boolean_t
dtj_check_pointer_pool(void)1278 dtj_check_pointer_pool(void)
1279 {
1280 	if (g_pointer_pool == NULL) {
1281 		g_pointer_pool = uu_list_pool_create("g_pointer_pool",
1282 		    sizeof (dtj_pointer_list_entry_t),
1283 		    offsetof(dtj_pointer_list_entry_t, dple_node),
1284 		    dtj_pointer_list_entry_cmp,
1285 		    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
1286 		if (g_pointer_pool == NULL) {
1287 			return (B_FALSE);
1288 		}
1289 	}
1290 	return (B_TRUE);
1291 }
1292 
1293 uu_list_t *
dtj_pointer_list_create(void)1294 dtj_pointer_list_create(void)
1295 {
1296 	uu_list_t *list;
1297 
1298 	if (!dtj_check_pointer_pool()) {
1299 		return (NULL);
1300 	}
1301 
1302 	list = uu_list_create(g_pointer_pool, NULL,
1303 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
1304 	return (list);
1305 }
1306 
1307 dtj_pointer_list_entry_t *
dtj_pointer_list_entry_create(void * p)1308 dtj_pointer_list_entry_create(void *p)
1309 {
1310 	dtj_pointer_list_entry_t *e;
1311 
1312 	if (!dtj_check_pointer_pool()) {
1313 		return (NULL);
1314 	}
1315 
1316 	e = uu_zalloc(sizeof (dtj_pointer_list_entry_t));
1317 	if (e) {
1318 		uu_list_node_init(e, &e->dple_node, g_pointer_pool);
1319 		e->dple_ptr = p;
1320 	}
1321 	return (e);
1322 }
1323 
1324 static boolean_t
dtj_check_string_pool(void)1325 dtj_check_string_pool(void)
1326 {
1327 	if (g_string_pool == NULL) {
1328 		g_string_pool = uu_list_pool_create("g_string_pool",
1329 		    sizeof (dtj_string_list_entry_t),
1330 		    offsetof(dtj_string_list_entry_t, dsle_node),
1331 		    dtj_string_list_entry_cmp,
1332 		    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
1333 		if (g_string_pool == NULL) {
1334 			return (B_FALSE);
1335 		}
1336 	}
1337 	return (B_TRUE);
1338 }
1339 
1340 uu_list_t *
dtj_string_list_create(void)1341 dtj_string_list_create(void)
1342 {
1343 	uu_list_t *list;
1344 
1345 	if (!dtj_check_string_pool()) {
1346 		return (NULL);
1347 	}
1348 
1349 	list = uu_list_create(g_string_pool, NULL,
1350 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
1351 	return (list);
1352 }
1353 
1354 dtj_string_list_entry_t *
dtj_string_list_entry_create(const char * s)1355 dtj_string_list_entry_create(const char *s)
1356 {
1357 	dtj_string_list_entry_t *e;
1358 
1359 	if (!dtj_check_string_pool()) {
1360 		return (NULL);
1361 	}
1362 
1363 	e = uu_zalloc(sizeof (dtj_string_list_entry_t));
1364 	if (e) {
1365 		uu_list_node_init(e, &e->dsle_node, g_string_pool);
1366 		if (s) {
1367 			e->dsle_value = malloc(strlen(s) + 1);
1368 			if (e->dsle_value) {
1369 				(void) strcpy(e->dsle_value, s);
1370 			} else {
1371 				uu_list_node_fini(e, &e->dsle_node,
1372 				    g_string_pool);
1373 				uu_free(e);
1374 				e = NULL;
1375 			}
1376 		}
1377 	}
1378 	return (e);
1379 }
1380 
1381 void
dtj_pointer_list_entry_destroy(void * v,dtj_value_destroy_f * value_destroy,void * arg)1382 dtj_pointer_list_entry_destroy(void *v,
1383     dtj_value_destroy_f *value_destroy, void *arg)
1384 {
1385 	if (v) {
1386 		dtj_pointer_list_entry_t *e = v;
1387 		if (value_destroy) {
1388 			value_destroy(e->dple_ptr, arg);
1389 		}
1390 		uu_list_node_fini(e, &e->dple_node, g_pointer_pool);
1391 		e->dple_ptr = NULL;
1392 		uu_free(v);
1393 	}
1394 }
1395 
1396 void
1397 /* ARGSUSED */
dtj_string_list_entry_destroy(void * v,void * arg)1398 dtj_string_list_entry_destroy(void *v, void *arg)
1399 {
1400 	if (v) {
1401 		dtj_string_list_entry_t *e = v;
1402 		free(e->dsle_value);
1403 		uu_list_node_fini(e, &e->dsle_node, g_string_pool);
1404 		e->dsle_value = NULL;
1405 		uu_free(v);
1406 	}
1407 }
1408 
1409 void
dtj_list_clear(uu_list_t * list,dtj_value_destroy_f * value_destroy,void * arg)1410 dtj_list_clear(uu_list_t *list, dtj_value_destroy_f *value_destroy,
1411     void *arg)
1412 {
1413 	void *cookie; /* needed for uu_list_teardown */
1414 	void *value;
1415 
1416 	if (!list) {
1417 		return;
1418 	}
1419 
1420 	cookie = NULL;
1421 	if (value_destroy) {
1422 		while ((value = uu_list_teardown(list, &cookie)) != NULL) {
1423 			value_destroy(value, arg);
1424 		}
1425 	} else {
1426 		while ((value = uu_list_teardown(list, &cookie)) != NULL) {
1427 		}
1428 	}
1429 }
1430 
1431 void
dtj_list_destroy(uu_list_t * list,dtj_value_destroy_f * value_destroy,void * arg)1432 dtj_list_destroy(uu_list_t *list,
1433     dtj_value_destroy_f *value_destroy, void *arg)
1434 {
1435 	dtj_list_clear(list, value_destroy, arg);
1436 	uu_list_destroy(list);
1437 }
1438 
1439 void
dtj_pointer_list_clear(uu_list_t * list,dtj_value_destroy_f * value_destroy,void * arg)1440 dtj_pointer_list_clear(uu_list_t *list,
1441     dtj_value_destroy_f *value_destroy, void *arg)
1442 {
1443 	void *cookie; /* needed for uu_list_teardown */
1444 	dtj_pointer_list_entry_t *e;
1445 
1446 	if (!list) {
1447 		return;
1448 	}
1449 
1450 	cookie = NULL;
1451 	while ((e = uu_list_teardown(list, &cookie)) != NULL) {
1452 		dtj_pointer_list_entry_destroy(e, value_destroy, arg);
1453 	}
1454 }
1455 
1456 void
dtj_pointer_list_destroy(uu_list_t * list,dtj_value_destroy_f * value_destroy,void * arg)1457 dtj_pointer_list_destroy(uu_list_t *list,
1458     dtj_value_destroy_f *value_destroy, void *arg)
1459 {
1460 	dtj_pointer_list_clear(list, value_destroy, arg);
1461 	uu_list_destroy(list);
1462 }
1463 
1464 void
dtj_string_list_clear(uu_list_t * list)1465 dtj_string_list_clear(uu_list_t *list)
1466 {
1467 	dtj_list_clear(list, dtj_string_list_entry_destroy, NULL);
1468 }
1469 
1470 void
dtj_string_list_destroy(uu_list_t * list)1471 dtj_string_list_destroy(uu_list_t *list)
1472 {
1473 	dtj_list_destroy(list, dtj_string_list_entry_destroy, NULL);
1474 }
1475 
1476 boolean_t
dtj_list_empty(uu_list_t * list)1477 dtj_list_empty(uu_list_t *list)
1478 {
1479 	return (uu_list_numnodes(list) == 0);
1480 }
1481 
1482 boolean_t
dtj_list_add(uu_list_t * list,void * value)1483 dtj_list_add(uu_list_t *list, void *value)
1484 {
1485 	return (uu_list_insert_before(list, NULL, value) == 0);
1486 }
1487 
1488 boolean_t
dtj_pointer_list_add(uu_list_t * list,void * p)1489 dtj_pointer_list_add(uu_list_t *list, void *p)
1490 {
1491 	dtj_pointer_list_entry_t *e = dtj_pointer_list_entry_create(p);
1492 	if (!e) {
1493 		return (B_FALSE);
1494 	}
1495 	return (dtj_list_add(list, e));
1496 }
1497 
1498 void *
dtj_pointer_list_walk_next(uu_list_walk_t * itr)1499 dtj_pointer_list_walk_next(uu_list_walk_t *itr)
1500 {
1501 	dtj_pointer_list_entry_t *e = uu_list_walk_next(itr);
1502 	if (!e) {
1503 		return (DTJ_INVALID_PTR);
1504 	}
1505 	return (e->dple_ptr);
1506 }
1507 
1508 void *
dtj_pointer_list_first(uu_list_t * list)1509 dtj_pointer_list_first(uu_list_t *list)
1510 {
1511 	dtj_pointer_list_entry_t *e = uu_list_first(list);
1512 	if (!e) {
1513 		/* NULL is a valid value; use -1 for invalid */
1514 		return (DTJ_INVALID_PTR);
1515 	}
1516 	return (e->dple_ptr);
1517 }
1518 
1519 void *
dtj_pointer_list_last(uu_list_t * list)1520 dtj_pointer_list_last(uu_list_t *list)
1521 {
1522 	dtj_pointer_list_entry_t *e = uu_list_last(list);
1523 	if (!e) {
1524 		/* NULL is a valid value; use -1 for invalid */
1525 		return (DTJ_INVALID_PTR);
1526 	}
1527 	return (e->dple_ptr);
1528 }
1529 
1530 boolean_t
dtj_string_list_add(uu_list_t * list,const char * s)1531 dtj_string_list_add(uu_list_t *list, const char *s)
1532 {
1533 	dtj_string_list_entry_t *e = dtj_string_list_entry_create(s);
1534 	if (!e) {
1535 		return (B_FALSE);
1536 	}
1537 	return (dtj_list_add(list, e));
1538 }
1539 
1540 const char *
dtj_string_list_walk_next(uu_list_walk_t * itr)1541 dtj_string_list_walk_next(uu_list_walk_t *itr)
1542 {
1543 	dtj_string_list_entry_t *e = uu_list_walk_next(itr);
1544 	if (!e) {
1545 		return (DTJ_INVALID_STR);
1546 	}
1547 	return (e->dsle_value);
1548 }
1549 
1550 const char *
dtj_string_list_first(uu_list_t * list)1551 dtj_string_list_first(uu_list_t *list)
1552 {
1553 	dtj_string_list_entry_t *e = uu_list_first(list);
1554 	if (!e) {
1555 		/* NULL is a valid string value; use -1 for invalid */
1556 		return (DTJ_INVALID_STR);
1557 	}
1558 	return (e->dsle_value);
1559 }
1560 
1561 const char *
dtj_string_list_last(uu_list_t * list)1562 dtj_string_list_last(uu_list_t *list)
1563 {
1564 	dtj_string_list_entry_t *e = uu_list_last(list);
1565 	if (!e) {
1566 		/* NULL is a valid string value; use -1 for invalid */
1567 		return (DTJ_INVALID_STR);
1568 	}
1569 	return (e->dsle_value);
1570 }
1571