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