blob: 0190d802c3e54699a871a8fa967bf564ae0ae71d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#include "util.h"
27#include "invoker.h"
28#include "eventHandler.h"
29#include "threadControl.h"
30#include "outStream.h"
31
32static jrawMonitorID invokerLock;
33
34void
35invoker_initialize(void)
36{
37 invokerLock = debugMonitorCreate("JDWP Invocation Lock");
38}
39
40void
41invoker_reset(void)
42{
43}
44
45void invoker_lock(void)
46{
47 debugMonitorEnter(invokerLock);
48}
49
50void invoker_unlock(void)
51{
52 debugMonitorExit(invokerLock);
53}
54
55static jbyte
56returnTypeTag(char *signature)
57{
58 char *tagPtr = strchr(signature, SIGNATURE_END_ARGS);
59 JDI_ASSERT(tagPtr);
60 tagPtr++; /* 1st character after the end of args */
61 return (jbyte)*tagPtr;
62}
63
64static jbyte
65nextArgumentTypeTag(void **cursor)
66{
67 char *tagPtr = *cursor;
68 jbyte argumentTag = (jbyte)*tagPtr;
69
70 if (*tagPtr != SIGNATURE_END_ARGS) {
71 /* Skip any array modifiers */
72 while (*tagPtr == JDWP_TAG(ARRAY)) {
73 tagPtr++;
74 }
75 /* Skip class name */
76 if (*tagPtr == JDWP_TAG(OBJECT)) {
77 tagPtr = strchr(tagPtr, SIGNATURE_END_CLASS) + 1;
78 JDI_ASSERT(tagPtr);
79 } else {
80 /* Skip primitive sig */
81 tagPtr++;
82 }
83 }
84
85 *cursor = tagPtr;
86 return argumentTag;
87}
88
89static jbyte
90firstArgumentTypeTag(char *signature, void **cursor)
91{
92 JDI_ASSERT(signature[0] == SIGNATURE_BEGIN_ARGS);
93 *cursor = signature + 1; /* skip to the first arg */
94 return nextArgumentTypeTag(cursor);
95}
96
97
98/*
99 * Note: argument refs may be destroyed on out-of-memory error
100 */
101static jvmtiError
102createGlobalRefs(JNIEnv *env, InvokeRequest *request)
103{
104 jvmtiError error;
105 jclass clazz = NULL;
106 jobject instance = NULL;
107 jint argIndex;
108 jbyte argumentTag;
109 jvalue *argument;
110 void *cursor;
111 jobject *argRefs = NULL;
112
113 error = JVMTI_ERROR_NONE;
114
115 if ( request->argumentCount > 0 ) {
116 /*LINTED*/
117 argRefs = jvmtiAllocate((jint)(request->argumentCount*sizeof(jobject)));
118 if ( argRefs==NULL ) {
119 error = AGENT_ERROR_OUT_OF_MEMORY;
120 } else {
121 /*LINTED*/
122 (void)memset(argRefs, 0, request->argumentCount*sizeof(jobject));
123 }
124 }
125
126 if ( error == JVMTI_ERROR_NONE ) {
127 saveGlobalRef(env, request->clazz, &clazz);
128 if (clazz == NULL) {
129 error = AGENT_ERROR_OUT_OF_MEMORY;
130 }
131 }
132
133 if ( error == JVMTI_ERROR_NONE && request->instance != NULL ) {
134 saveGlobalRef(env, request->instance, &instance);
135 if (instance == NULL) {
136 error = AGENT_ERROR_OUT_OF_MEMORY;
137 }
138 }
139
140 if ( error == JVMTI_ERROR_NONE && argRefs!=NULL ) {
141 argIndex = 0;
142 argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor);
143 argument = request->arguments;
144 while (argumentTag != SIGNATURE_END_ARGS) {
145 if ( argIndex > request->argumentCount ) {
146 break;
147 }
148 if ((argumentTag == JDWP_TAG(OBJECT)) ||
149 (argumentTag == JDWP_TAG(ARRAY))) {
150 /* Create a global ref for any non-null argument */
151 if (argument->l != NULL) {
152 saveGlobalRef(env, argument->l, &argRefs[argIndex]);
153 if (argRefs[argIndex] == NULL) {
154 error = AGENT_ERROR_OUT_OF_MEMORY;
155 break;
156 }
157 }
158 }
159 argument++;
160 argIndex++;
161 argumentTag = nextArgumentTypeTag(&cursor);
162 }
163 }
164
165#ifdef FIXUP /* Why isn't this an error? */
166 /* Make sure the argument count matches */
167 if ( error == JVMTI_ERROR_NONE && argIndex != request->argumentCount ) {
168 error = AGENT_ERROR_INVALID_COUNT;
169 }
170#endif
171
172 /* Finally, put the global refs into the request if no errors */
173 if ( error == JVMTI_ERROR_NONE ) {
174 request->clazz = clazz;
175 request->instance = instance;
176 if ( argRefs!=NULL ) {
177 argIndex = 0;
178 argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor);
179 argument = request->arguments;
180 while ( argIndex < request->argumentCount ) {
181 if ((argumentTag == JDWP_TAG(OBJECT)) ||
182 (argumentTag == JDWP_TAG(ARRAY))) {
183 argument->l = argRefs[argIndex];
184 }
185 argument++;
186 argIndex++;
187 argumentTag = nextArgumentTypeTag(&cursor);
188 }
189 jvmtiDeallocate(argRefs);
190 }
191 return JVMTI_ERROR_NONE;
192
193 } else {
194 /* Delete global references */
195 if ( clazz != NULL ) {
196 tossGlobalRef(env, &clazz);
197 }
198 if ( instance != NULL ) {
199 tossGlobalRef(env, &instance);
200 }
201 if ( argRefs!=NULL ) {
202 for ( argIndex=0; argIndex < request->argumentCount; argIndex++ ) {
203 if ( argRefs[argIndex] != NULL ) {
204 tossGlobalRef(env, &argRefs[argIndex]);
205 }
206 }
207 jvmtiDeallocate(argRefs);
208 }
209 }
210
211 return error;
212}
213
214static jvmtiError
215fillInvokeRequest(JNIEnv *env, InvokeRequest *request,
216 jbyte invokeType, jbyte options, jint id,
217 jthread thread, jclass clazz, jmethodID method,
218 jobject instance,
219 jvalue *arguments, jint argumentCount)
220{
221 jvmtiError error;
222 if (!request->available) {
223 /*
224 * Thread is not at a point where it can invoke.
225 */
226 return AGENT_ERROR_INVALID_THREAD;
227 }
228 if (request->pending) {
229 /*
230 * Pending invoke
231 */
232 return AGENT_ERROR_ALREADY_INVOKING;
233 }
234
235 request->invokeType = invokeType;
236 request->options = options;
237 request->detached = JNI_FALSE;
238 request->id = id;
239 request->clazz = clazz;
240 request->method = method;
241 request->instance = instance;
242 request->arguments = arguments;
243 request->arguments = arguments;
244 request->argumentCount = argumentCount;
245
246 request->returnValue.j = 0;
247 request->exception = 0;
248
249 /*
250 * Squirrel away the method signature
251 */
252 error = methodSignature(method, NULL, &request->methodSignature, NULL);
253 if (error != JVMTI_ERROR_NONE) {
254 return error;
255 }
256
257 /*
258 * The given references for class and instance are not guaranteed
259 * to be around long enough for invocation, so create new ones
260 * here.
261 */
262 error = createGlobalRefs(env, request);
263 if (error != JVMTI_ERROR_NONE) {
264 jvmtiDeallocate(request->methodSignature);
265 return error;
266 }
267
268 request->pending = JNI_TRUE;
269 request->available = JNI_FALSE;
270 return JVMTI_ERROR_NONE;
271}
272
273void
274invoker_enableInvokeRequests(jthread thread)
275{
276 InvokeRequest *request;
277
278 JDI_ASSERT(thread);
279
280 request = threadControl_getInvokeRequest(thread);
281 if (request == NULL) {
282 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request");
283 }
284
285 request->available = JNI_TRUE;
286}
287
288jvmtiError
289invoker_requestInvoke(jbyte invokeType, jbyte options, jint id,
290 jthread thread, jclass clazz, jmethodID method,
291 jobject instance,
292 jvalue *arguments, jint argumentCount)
293{
294 JNIEnv *env = getEnv();
295 InvokeRequest *request;
296 jvmtiError error = JVMTI_ERROR_NONE;
297
298 debugMonitorEnter(invokerLock);
299 request = threadControl_getInvokeRequest(thread);
300 if (request != NULL) {
301 error = fillInvokeRequest(env, request, invokeType, options, id,
302 thread, clazz, method, instance,
303 arguments, argumentCount);
304 }
305 debugMonitorExit(invokerLock);
306
307 if (error == JVMTI_ERROR_NONE) {
308 if (options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED) ) {
309 /* true means it is okay to unblock the commandLoop thread */
310 (void)threadControl_resumeThread(thread, JNI_TRUE);
311 } else {
312 (void)threadControl_resumeAll();
313 }
314 }
315
316 return error;
317}
318
319static void
320invokeConstructor(JNIEnv *env, InvokeRequest *request)
321{
322 jobject object;
323 object = JNI_FUNC_PTR(env,NewObjectA)(env, request->clazz,
324 request->method,
325 request->arguments);
326 request->returnValue.l = NULL;
327 if (object != NULL) {
328 saveGlobalRef(env, object, &(request->returnValue.l));
329 }
330}
331
332static void
333invokeStatic(JNIEnv *env, InvokeRequest *request)
334{
335 switch(returnTypeTag(request->methodSignature)) {
336 case JDWP_TAG(OBJECT):
337 case JDWP_TAG(ARRAY): {
338 jobject object;
339 object = JNI_FUNC_PTR(env,CallStaticObjectMethodA)(env,
340 request->clazz,
341 request->method,
342 request->arguments);
343 request->returnValue.l = NULL;
344 if (object != NULL) {
345 saveGlobalRef(env, object, &(request->returnValue.l));
346 }
347 break;
348 }
349
350
351 case JDWP_TAG(BYTE):
352 request->returnValue.b = JNI_FUNC_PTR(env,CallStaticByteMethodA)(env,
353 request->clazz,
354 request->method,
355 request->arguments);
356 break;
357
358 case JDWP_TAG(CHAR):
359 request->returnValue.c = JNI_FUNC_PTR(env,CallStaticCharMethodA)(env,
360 request->clazz,
361 request->method,
362 request->arguments);
363 break;
364
365 case JDWP_TAG(FLOAT):
366 request->returnValue.f = JNI_FUNC_PTR(env,CallStaticFloatMethodA)(env,
367 request->clazz,
368 request->method,
369 request->arguments);
370 break;
371
372 case JDWP_TAG(DOUBLE):
373 request->returnValue.d = JNI_FUNC_PTR(env,CallStaticDoubleMethodA)(env,
374 request->clazz,
375 request->method,
376 request->arguments);
377 break;
378
379 case JDWP_TAG(INT):
380 request->returnValue.i = JNI_FUNC_PTR(env,CallStaticIntMethodA)(env,
381 request->clazz,
382 request->method,
383 request->arguments);
384 break;
385
386 case JDWP_TAG(LONG):
387 request->returnValue.j = JNI_FUNC_PTR(env,CallStaticLongMethodA)(env,
388 request->clazz,
389 request->method,
390 request->arguments);
391 break;
392
393 case JDWP_TAG(SHORT):
394 request->returnValue.s = JNI_FUNC_PTR(env,CallStaticShortMethodA)(env,
395 request->clazz,
396 request->method,
397 request->arguments);
398 break;
399
400 case JDWP_TAG(BOOLEAN):
401 request->returnValue.z = JNI_FUNC_PTR(env,CallStaticBooleanMethodA)(env,
402 request->clazz,
403 request->method,
404 request->arguments);
405 break;
406
407 case JDWP_TAG(VOID):
408 JNI_FUNC_PTR(env,CallStaticVoidMethodA)(env,
409 request->clazz,
410 request->method,
411 request->arguments);
412 break;
413
414 default:
415 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature");
416 break;
417 }
418}
419
420static void
421invokeVirtual(JNIEnv *env, InvokeRequest *request)
422{
423 switch(returnTypeTag(request->methodSignature)) {
424 case JDWP_TAG(OBJECT):
425 case JDWP_TAG(ARRAY): {
426 jobject object;
427 object = JNI_FUNC_PTR(env,CallObjectMethodA)(env,
428 request->instance,
429 request->method,
430 request->arguments);
431 request->returnValue.l = NULL;
432 if (object != NULL) {
433 saveGlobalRef(env, object, &(request->returnValue.l));
434 }
435 break;
436 }
437
438 case JDWP_TAG(BYTE):
439 request->returnValue.b = JNI_FUNC_PTR(env,CallByteMethodA)(env,
440 request->instance,
441 request->method,
442 request->arguments);
443 break;
444
445 case JDWP_TAG(CHAR):
446 request->returnValue.c = JNI_FUNC_PTR(env,CallCharMethodA)(env,
447 request->instance,
448 request->method,
449 request->arguments);
450 break;
451
452 case JDWP_TAG(FLOAT):
453 request->returnValue.f = JNI_FUNC_PTR(env,CallFloatMethodA)(env,
454 request->instance,
455 request->method,
456 request->arguments);
457 break;
458
459 case JDWP_TAG(DOUBLE):
460 request->returnValue.d = JNI_FUNC_PTR(env,CallDoubleMethodA)(env,
461 request->instance,
462 request->method,
463 request->arguments);
464 break;
465
466 case JDWP_TAG(INT):
467 request->returnValue.i = JNI_FUNC_PTR(env,CallIntMethodA)(env,
468 request->instance,
469 request->method,
470 request->arguments);
471 break;
472
473 case JDWP_TAG(LONG):
474 request->returnValue.j = JNI_FUNC_PTR(env,CallLongMethodA)(env,
475 request->instance,
476 request->method,
477 request->arguments);
478 break;
479
480 case JDWP_TAG(SHORT):
481 request->returnValue.s = JNI_FUNC_PTR(env,CallShortMethodA)(env,
482 request->instance,
483 request->method,
484 request->arguments);
485 break;
486
487 case JDWP_TAG(BOOLEAN):
488 request->returnValue.z = JNI_FUNC_PTR(env,CallBooleanMethodA)(env,
489 request->instance,
490 request->method,
491 request->arguments);
492 break;
493
494 case JDWP_TAG(VOID):
495 JNI_FUNC_PTR(env,CallVoidMethodA)(env,
496 request->instance,
497 request->method,
498 request->arguments);
499 break;
500
501 default:
502 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature");
503 break;
504 }
505}
506
507static void
508invokeNonvirtual(JNIEnv *env, InvokeRequest *request)
509{
510 switch(returnTypeTag(request->methodSignature)) {
511 case JDWP_TAG(OBJECT):
512 case JDWP_TAG(ARRAY): {
513 jobject object;
514 object = JNI_FUNC_PTR(env,CallNonvirtualObjectMethodA)(env,
515 request->instance,
516 request->clazz,
517 request->method,
518 request->arguments);
519 request->returnValue.l = NULL;
520 if (object != NULL) {
521 saveGlobalRef(env, object, &(request->returnValue.l));
522 }
523 break;
524 }
525
526 case JDWP_TAG(BYTE):
527 request->returnValue.b = JNI_FUNC_PTR(env,CallNonvirtualByteMethodA)(env,
528 request->instance,
529 request->clazz,
530 request->method,
531 request->arguments);
532 break;
533
534 case JDWP_TAG(CHAR):
535 request->returnValue.c = JNI_FUNC_PTR(env,CallNonvirtualCharMethodA)(env,
536 request->instance,
537 request->clazz,
538 request->method,
539 request->arguments);
540 break;
541
542 case JDWP_TAG(FLOAT):
543 request->returnValue.f = JNI_FUNC_PTR(env,CallNonvirtualFloatMethodA)(env,
544 request->instance,
545 request->clazz,
546 request->method,
547 request->arguments);
548 break;
549
550 case JDWP_TAG(DOUBLE):
551 request->returnValue.d = JNI_FUNC_PTR(env,CallNonvirtualDoubleMethodA)(env,
552 request->instance,
553 request->clazz,
554 request->method,
555 request->arguments);
556 break;
557
558 case JDWP_TAG(INT):
559 request->returnValue.i = JNI_FUNC_PTR(env,CallNonvirtualIntMethodA)(env,
560 request->instance,
561 request->clazz,
562 request->method,
563 request->arguments);
564 break;
565
566 case JDWP_TAG(LONG):
567 request->returnValue.j = JNI_FUNC_PTR(env,CallNonvirtualLongMethodA)(env,
568 request->instance,
569 request->clazz,
570 request->method,
571 request->arguments);
572 break;
573
574 case JDWP_TAG(SHORT):
575 request->returnValue.s = JNI_FUNC_PTR(env,CallNonvirtualShortMethodA)(env,
576 request->instance,
577 request->clazz,
578 request->method,
579 request->arguments);
580 break;
581
582 case JDWP_TAG(BOOLEAN):
583 request->returnValue.z = JNI_FUNC_PTR(env,CallNonvirtualBooleanMethodA)(env,
584 request->instance,
585 request->clazz,
586 request->method,
587 request->arguments);
588 break;
589
590 case JDWP_TAG(VOID):
591 JNI_FUNC_PTR(env,CallNonvirtualVoidMethodA)(env,
592 request->instance,
593 request->clazz,
594 request->method,
595 request->arguments);
596 break;
597
598 default:
599 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature");
600 break;
601 }
602}
603
604jboolean
605invoker_doInvoke(jthread thread)
606{
607 JNIEnv *env;
608 jboolean startNow;
609 InvokeRequest *request;
610
611 JDI_ASSERT(thread);
612
613 debugMonitorEnter(invokerLock);
614
615 request = threadControl_getInvokeRequest(thread);
616 if (request == NULL) {
617 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request");
618 }
619
620 request->available = JNI_FALSE;
621 startNow = request->pending && !request->started;
622
623 if (startNow) {
624 request->started = JNI_TRUE;
625 }
626 debugMonitorExit(invokerLock);
627
628 if (!startNow) {
629 return JNI_FALSE;
630 }
631
632 env = getEnv();
633
634 WITH_LOCAL_REFS(env, 2) { /* 1 for obj return values, 1 for exception */
635
636 jobject exception;
637
638 JNI_FUNC_PTR(env,ExceptionClear)(env);
639
640 switch (request->invokeType) {
641 case INVOKE_CONSTRUCTOR:
642 invokeConstructor(env, request);
643 break;
644 case INVOKE_STATIC:
645 invokeStatic(env, request);
646 break;
647 case INVOKE_INSTANCE:
648 if (request->options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) {
649 invokeNonvirtual(env, request);
650 } else {
651 invokeVirtual(env, request);
652 }
653 break;
654 default:
655 JDI_ASSERT(JNI_FALSE);
656 }
657 request->exception = NULL;
658 exception = JNI_FUNC_PTR(env,ExceptionOccurred)(env);
659 if (exception != NULL) {
660 JNI_FUNC_PTR(env,ExceptionClear)(env);
661 saveGlobalRef(env, exception, &(request->exception));
662 }
663
664 } END_WITH_LOCAL_REFS(env);
665
666 return JNI_TRUE;
667}
668
669void
670invoker_completeInvokeRequest(jthread thread)
671{
672 JNIEnv *env = getEnv();
673 PacketOutputStream out;
674 jbyte tag;
675 jobject exc;
676 jvalue returnValue;
677 jint id;
678 InvokeRequest *request;
679 jboolean detached;
680
681 JDI_ASSERT(thread);
682
683 /* Prevent gcc errors on uninitialized variables. */
684 tag = 0;
685 exc = NULL;
686 id = 0;
687
688 eventHandler_lock(); /* for proper lock order */
689 debugMonitorEnter(invokerLock);
690
691 request = threadControl_getInvokeRequest(thread);
692 if (request == NULL) {
693 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request");
694 }
695
696 JDI_ASSERT(request->pending);
697 JDI_ASSERT(request->started);
698
699 request->pending = JNI_FALSE;
700 request->started = JNI_FALSE;
701 request->available = JNI_TRUE; /* For next time around */
702
703 detached = request->detached;
704 if (!detached) {
705 if (request->options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED)) {
706 (void)threadControl_suspendThread(thread, JNI_FALSE);
707 } else {
708 (void)threadControl_suspendAll();
709 }
710
711 if (request->invokeType == INVOKE_CONSTRUCTOR) {
712 /*
713 * Although constructors technically have a return type of
714 * void, we return the object created.
715 */
716 tag = specificTypeKey(env, request->returnValue.l);
717 } else {
718 tag = returnTypeTag(request->methodSignature);
719 }
720 id = request->id;
721 exc = request->exception;
722 returnValue = request->returnValue;
723 }
724
725 /*
726 * Give up the lock before I/O operation
727 */
728 debugMonitorExit(invokerLock);
729 eventHandler_unlock();
730
731
732 if (!detached) {
733 outStream_initReply(&out, id);
734 (void)outStream_writeValue(env, &out, tag, returnValue);
735 (void)outStream_writeObjectTag(env, &out, exc);
736 (void)outStream_writeObjectRef(env, &out, exc);
737 outStream_sendReply(&out);
738 }
739}
740
741jboolean
742invoker_isPending(jthread thread)
743{
744 InvokeRequest *request;
745
746 JDI_ASSERT(thread);
747 request = threadControl_getInvokeRequest(thread);
748 if (request == NULL) {
749 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request");
750 }
751 return request->pending;
752}
753
754jboolean
755invoker_isEnabled(jthread thread)
756{
757 InvokeRequest *request;
758
759 JDI_ASSERT(thread);
760 request = threadControl_getInvokeRequest(thread);
761 if (request == NULL) {
762 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request");
763 }
764 return request->available;
765}
766
767void
768invoker_detach(InvokeRequest *request)
769{
770 JDI_ASSERT(request);
771 debugMonitorEnter(invokerLock);
772 request->detached = JNI_TRUE;
773 debugMonitorExit(invokerLock);
774}