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