1/*
2 * Copyright (c) 1998, 2016, Oracle and/or its affiliates. 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "util.h"
27#include "VirtualMachineImpl.h"
28#include "commonRef.h"
29#include "inStream.h"
30#include "outStream.h"
31#include "eventHandler.h"
32#include "eventHelper.h"
33#include "threadControl.h"
34#include "SDE.h"
35#include "FrameID.h"
36
37static char *versionName = "Java Debug Wire Protocol (Reference Implementation)";
38static int majorVersion = 9;  /* JDWP major version */
39static int minorVersion = 0;  /* JDWP minor version */
40
41static jboolean
42version(PacketInputStream *in, PacketOutputStream *out)
43{
44    char buf[500];
45    char *vmName;
46    char *vmVersion;
47    char *vmInfo;
48
49    if (gdata->vmDead) {
50        outStream_setError(out, JDWP_ERROR(VM_DEAD));
51        return JNI_TRUE;
52    }
53
54    vmVersion = gdata->property_java_version;
55    if (vmVersion == NULL) {
56        vmVersion = "<unknown>";
57    }
58    vmName = gdata->property_java_vm_name;
59    if (vmName == NULL) {
60        vmName = "<unknown>";
61    }
62    vmInfo = gdata->property_java_vm_info;
63    if (vmInfo == NULL) {
64        vmInfo = "<unknown>";
65    }
66
67    /*
68     * Write the descriptive version information
69     */
70    (void)snprintf(buf, sizeof(buf),
71                "%s version %d.%d\nJVM Debug Interface version %d.%d\n"
72                 "JVM version %s (%s, %s)",
73                  versionName, majorVersion, minorVersion,
74                  jvmtiMajorVersion(), jvmtiMinorVersion(),
75                  vmVersion, vmName, vmInfo);
76    (void)outStream_writeString(out, buf);
77
78    /*
79     * Write the JDWP version numbers
80     */
81    (void)outStream_writeInt(out, majorVersion);
82    (void)outStream_writeInt(out, minorVersion);
83
84    /*
85     * Write the VM version and name
86     */
87    (void)outStream_writeString(out, vmVersion);
88    (void)outStream_writeString(out, vmName);
89
90    return JNI_TRUE;
91}
92
93static jboolean
94classesForSignature(PacketInputStream *in, PacketOutputStream *out)
95{
96    JNIEnv *env;
97    char *signature;
98
99    if (gdata->vmDead) {
100        outStream_setError(out, JDWP_ERROR(VM_DEAD));
101        return JNI_TRUE;
102    }
103
104    signature = inStream_readString(in);
105    if (signature == NULL) {
106        outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
107        return JNI_TRUE;
108    }
109    if (inStream_error(in)) {
110        return JNI_TRUE;
111    }
112
113    env = getEnv();
114
115    WITH_LOCAL_REFS(env, 1) {
116
117        jint classCount;
118        jclass *theClasses;
119        jvmtiError error;
120
121        error = allLoadedClasses(&theClasses, &classCount);
122        if ( error == JVMTI_ERROR_NONE ) {
123            /* Count classes in theClasses which match signature */
124            int matchCount = 0;
125            /* Count classes written to the JDWP connection */
126            int writtenCount = 0;
127            int i;
128
129            for (i = 0; i < classCount; i++) {
130                jclass clazz = theClasses[i];
131                jint status = classStatus(clazz);
132                char *candidate_signature = NULL;
133                jint wanted =
134                    (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY|
135                     JVMTI_CLASS_STATUS_PRIMITIVE);
136
137                /* We want prepared classes, primitives, and arrays only */
138                if ((status & wanted) == 0) {
139                    continue;
140                }
141
142                error = classSignature(clazz, &candidate_signature, NULL);
143                if (error != JVMTI_ERROR_NONE) {
144                  // Clazz become invalid since the time we get the class list
145                  // Skip this entry
146                  if (error == JVMTI_ERROR_INVALID_CLASS) {
147                    continue;
148                  }
149
150                  break;
151                }
152
153                if (strcmp(candidate_signature, signature) == 0) {
154                    /* Float interesting classes (those that
155                     * are matching and are prepared) to the
156                     * beginning of the array.
157                     */
158                    theClasses[i] = theClasses[matchCount];
159                    theClasses[matchCount++] = clazz;
160                }
161                jvmtiDeallocate(candidate_signature);
162            }
163
164            /* At this point matching prepared classes occupy
165             * indicies 0 thru matchCount-1 of theClasses.
166             */
167
168            if ( error ==  JVMTI_ERROR_NONE ) {
169                (void)outStream_writeInt(out, matchCount);
170                for (; writtenCount < matchCount; writtenCount++) {
171                    jclass clazz = theClasses[writtenCount];
172                    jint status = classStatus(clazz);
173                    jbyte tag = referenceTypeTag(clazz);
174                    (void)outStream_writeByte(out, tag);
175                    (void)outStream_writeObjectRef(env, out, clazz);
176                    (void)outStream_writeInt(out, map2jdwpClassStatus(status));
177                    /* No point in continuing if there's an error */
178                    if (outStream_error(out)) {
179                        break;
180                    }
181                }
182            }
183
184            jvmtiDeallocate(theClasses);
185        }
186
187        if ( error != JVMTI_ERROR_NONE ) {
188            outStream_setError(out, map2jdwpError(error));
189        }
190
191    } END_WITH_LOCAL_REFS(env);
192
193    jvmtiDeallocate(signature);
194
195    return JNI_TRUE;
196}
197
198static jboolean
199allModules(PacketInputStream *in, PacketOutputStream *out)
200{
201    JNIEnv *env;
202
203    if (gdata->vmDead) {
204        outStream_setError(out, JDWP_ERROR(VM_DEAD));
205        return JNI_TRUE;
206    }
207
208    env = getEnv();
209
210    WITH_LOCAL_REFS(env, 1) {
211
212        jint count = 0;
213        jint i = 0;
214        jobject* modules = NULL;
215        jvmtiError error = JVMTI_ERROR_NONE;
216
217        error = JVMTI_FUNC_PTR(gdata->jvmti, GetAllModules) (gdata->jvmti, &count, &modules);
218        if (error != JVMTI_ERROR_NONE) {
219            outStream_setError(out, map2jdwpError(error));
220        } else {
221            (void)outStream_writeInt(out, count);
222            for (i = 0; i < count; i++) {
223                (void)outStream_writeModuleRef(env, out, modules[i]);
224            }
225            jvmtiDeallocate(modules);
226        }
227
228    } END_WITH_LOCAL_REFS(env);
229
230    return JNI_TRUE;
231}
232
233static jboolean
234allClasses1(PacketInputStream *in, PacketOutputStream *out, int outputGenerics)
235{
236    JNIEnv *env;
237
238    if (gdata->vmDead) {
239        outStream_setError(out, JDWP_ERROR(VM_DEAD));
240        return JNI_TRUE;
241    }
242
243    env = getEnv();
244
245    WITH_LOCAL_REFS(env, 1) {
246
247        jint classCount;
248        jclass *theClasses;
249        jvmtiError error;
250
251        error = allLoadedClasses(&theClasses, &classCount);
252        if ( error != JVMTI_ERROR_NONE ) {
253            outStream_setError(out, map2jdwpError(error));
254        } else {
255            /* Count classes in theClasses which are prepared */
256            int prepCount = 0;
257            /* Count classes written to the JDWP connection */
258            int writtenCount = 0;
259            int i;
260
261            for (i=0; i<classCount; i++) {
262                jclass clazz = theClasses[i];
263                jint status = classStatus(clazz);
264                jint wanted =
265                    (JVMTI_CLASS_STATUS_PREPARED|JVMTI_CLASS_STATUS_ARRAY);
266
267                /* We want prepared classes and arrays only */
268                if ((status & wanted) != 0) {
269                    /* Float interesting classes (those that
270                     * are prepared) to the beginning of the array.
271                     */
272                    theClasses[i] = theClasses[prepCount];
273                    theClasses[prepCount++] = clazz;
274                }
275            }
276
277            /* At this point prepared classes occupy
278             * indicies 0 thru prepCount-1 of theClasses.
279             */
280
281            (void)outStream_writeInt(out, prepCount);
282            for (; writtenCount < prepCount; writtenCount++) {
283                char *signature = NULL;
284                char *genericSignature = NULL;
285                jclass clazz = theClasses[writtenCount];
286                jint status = classStatus(clazz);
287                jbyte tag = referenceTypeTag(clazz);
288                jvmtiError error;
289
290                error = classSignature(clazz, &signature, &genericSignature);
291                if (error != JVMTI_ERROR_NONE) {
292                    outStream_setError(out, map2jdwpError(error));
293                    break;
294                }
295
296                (void)outStream_writeByte(out, tag);
297                (void)outStream_writeObjectRef(env, out, clazz);
298                (void)outStream_writeString(out, signature);
299                if (outputGenerics == 1) {
300                    writeGenericSignature(out, genericSignature);
301                }
302
303                (void)outStream_writeInt(out, map2jdwpClassStatus(status));
304                jvmtiDeallocate(signature);
305                if (genericSignature != NULL) {
306                  jvmtiDeallocate(genericSignature);
307                }
308
309                /* No point in continuing if there's an error */
310                if (outStream_error(out)) {
311                    break;
312                }
313            }
314            jvmtiDeallocate(theClasses);
315        }
316
317    } END_WITH_LOCAL_REFS(env);
318
319    return JNI_TRUE;
320}
321
322static jboolean
323allClasses(PacketInputStream *in, PacketOutputStream *out)
324{
325    return allClasses1(in, out, 0);
326}
327
328static jboolean
329allClassesWithGeneric(PacketInputStream *in, PacketOutputStream *out)
330{
331    return allClasses1(in, out, 1);
332}
333
334  /***********************************************************/
335
336
337static jboolean
338instanceCounts(PacketInputStream *in, PacketOutputStream *out)
339{
340    jint classCount;
341    jclass *classes;
342    JNIEnv *env;
343    int ii;
344
345    if (gdata->vmDead) {
346        outStream_setError(out, JDWP_ERROR(VM_DEAD));
347        return JNI_TRUE;
348    }
349
350    classCount = inStream_readInt(in);
351
352    if (inStream_error(in)) {
353        return JNI_TRUE;
354    }
355    if (classCount == 0) {
356        (void)outStream_writeInt(out, 0);
357        return JNI_TRUE;
358    }
359    if (classCount < 0) {
360        outStream_setError(out, JDWP_ERROR(ILLEGAL_ARGUMENT));
361        return JNI_TRUE;
362    }
363    env = getEnv();
364    classes = jvmtiAllocate(classCount * (int)sizeof(jclass));
365    for (ii = 0; ii < classCount; ii++) {
366        jdwpError errorCode;
367        classes[ii] = inStream_readClassRef(env, in);
368        errorCode = inStream_error(in);
369        if (errorCode != JDWP_ERROR(NONE)) {
370            /*
371             * A class could have been unloaded/gc'd so
372             * if we get an error, just ignore it and keep
373             * going.  An instanceCount of 0 will be returned.
374             */
375            if (errorCode == JDWP_ERROR(INVALID_OBJECT) ||
376                errorCode == JDWP_ERROR(INVALID_CLASS)) {
377                inStream_clearError(in);
378                classes[ii] = NULL;
379                continue;
380            }
381            jvmtiDeallocate(classes);
382            return JNI_TRUE;
383        }
384    }
385
386    WITH_LOCAL_REFS(env, 1) {
387        jlong      *counts;
388        jvmtiError error;
389
390        counts = jvmtiAllocate(classCount * (int)sizeof(jlong));
391        /* Iterate over heap getting info on these classes */
392        error = classInstanceCounts(classCount, classes, counts);
393        if (error != JVMTI_ERROR_NONE) {
394            outStream_setError(out, map2jdwpError(error));
395        } else {
396            (void)outStream_writeInt(out, classCount);
397            for (ii = 0; ii < classCount; ii++) {
398                (void)outStream_writeLong(out, counts[ii]);
399            }
400        }
401        jvmtiDeallocate(counts);
402    } END_WITH_LOCAL_REFS(env);
403    jvmtiDeallocate(classes);
404    return JNI_TRUE;
405}
406
407static jboolean
408redefineClasses(PacketInputStream *in, PacketOutputStream *out)
409{
410    jvmtiClassDefinition *classDefs;
411    jboolean ok = JNI_TRUE;
412    jint classCount;
413    jint i;
414    JNIEnv *env;
415
416    if (gdata->vmDead) {
417        /* quietly ignore */
418        return JNI_TRUE;
419    }
420
421    classCount = inStream_readInt(in);
422    if (inStream_error(in)) {
423        return JNI_TRUE;
424    }
425    if ( classCount == 0 ) {
426        return JNI_TRUE;
427    }
428    /*LINTED*/
429    classDefs = jvmtiAllocate(classCount*(int)sizeof(jvmtiClassDefinition));
430    if (classDefs == NULL) {
431        outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
432        return JNI_TRUE;
433    }
434    /*LINTED*/
435    (void)memset(classDefs, 0, classCount*sizeof(jvmtiClassDefinition));
436
437    env = getEnv();
438    for (i = 0; i < classCount; ++i) {
439        int byteCount;
440        unsigned char * bytes;
441        jclass clazz;
442
443        clazz = inStream_readClassRef(env, in);
444        if (inStream_error(in)) {
445            ok = JNI_FALSE;
446            break;
447        }
448        byteCount = inStream_readInt(in);
449        if (inStream_error(in)) {
450            ok = JNI_FALSE;
451            break;
452        }
453        if ( byteCount <= 0 ) {
454            outStream_setError(out, JDWP_ERROR(INVALID_CLASS_FORMAT));
455            ok = JNI_FALSE;
456            break;
457        }
458        bytes = (unsigned char *)jvmtiAllocate(byteCount);
459        if (bytes == NULL) {
460            outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
461            ok = JNI_FALSE;
462            break;
463        }
464        (void)inStream_readBytes(in, byteCount, (jbyte *)bytes);
465        if (inStream_error(in)) {
466            ok = JNI_FALSE;
467            break;
468        }
469
470        classDefs[i].klass = clazz;
471        classDefs[i].class_byte_count = byteCount;
472        classDefs[i].class_bytes = bytes;
473    }
474
475    if (ok == JNI_TRUE) {
476        jvmtiError error;
477
478        error = JVMTI_FUNC_PTR(gdata->jvmti,RedefineClasses)
479                        (gdata->jvmti, classCount, classDefs);
480        if (error != JVMTI_ERROR_NONE) {
481            outStream_setError(out, map2jdwpError(error));
482        } else {
483            /* zap our BP info */
484            for ( i = 0 ; i < classCount; i++ ) {
485                eventHandler_freeClassBreakpoints(classDefs[i].klass);
486            }
487        }
488    }
489
490    /* free up allocated memory */
491    for ( i = 0 ; i < classCount; i++ ) {
492        if ( classDefs[i].class_bytes != NULL ) {
493            jvmtiDeallocate((void*)classDefs[i].class_bytes);
494        }
495    }
496    jvmtiDeallocate(classDefs);
497
498    return JNI_TRUE;
499}
500
501static jboolean
502setDefaultStratum(PacketInputStream *in, PacketOutputStream *out)
503{
504    char *stratumId;
505
506    if (gdata->vmDead) {
507        /* quietly ignore */
508        return JNI_TRUE;
509    }
510
511    stratumId = inStream_readString(in);
512    if (inStream_error(in)) {
513        return JNI_TRUE;
514    } else if (strcmp(stratumId, "") == 0) {
515        stratumId = NULL;
516    }
517    setGlobalStratumId(stratumId);
518
519    return JNI_TRUE;
520}
521
522static jboolean
523getAllThreads(PacketInputStream *in, PacketOutputStream *out)
524{
525    JNIEnv *env;
526
527    if (gdata->vmDead) {
528        outStream_setError(out, JDWP_ERROR(VM_DEAD));
529        return JNI_TRUE;
530    }
531
532    env = getEnv();
533
534    WITH_LOCAL_REFS(env, 1) {
535
536        int i;
537        jint threadCount;
538        jthread *theThreads;
539
540        theThreads = allThreads(&threadCount);
541        if (theThreads == NULL) {
542            outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
543        } else {
544            /* Squish out all of the debugger-spawned threads */
545            threadCount = filterDebugThreads(theThreads, threadCount);
546
547            (void)outStream_writeInt(out, threadCount);
548            for (i = 0; i <threadCount; i++) {
549                (void)outStream_writeObjectRef(env, out, theThreads[i]);
550            }
551
552            jvmtiDeallocate(theThreads);
553        }
554
555    } END_WITH_LOCAL_REFS(env);
556
557    return JNI_TRUE;
558}
559
560static jboolean
561topLevelThreadGroups(PacketInputStream *in, PacketOutputStream *out)
562{
563    JNIEnv *env;
564
565    if (gdata->vmDead) {
566        outStream_setError(out, JDWP_ERROR(VM_DEAD));
567        return JNI_TRUE;
568    }
569
570    env = getEnv();
571
572    WITH_LOCAL_REFS(env, 1) {
573
574        jvmtiError error;
575        jint groupCount;
576        jthreadGroup *groups;
577
578        groups = NULL;
579        error = JVMTI_FUNC_PTR(gdata->jvmti,GetTopThreadGroups)
580                    (gdata->jvmti, &groupCount, &groups);
581        if (error != JVMTI_ERROR_NONE) {
582            outStream_setError(out, map2jdwpError(error));
583        } else {
584            int i;
585
586            (void)outStream_writeInt(out, groupCount);
587            for (i = 0; i < groupCount; i++) {
588                (void)outStream_writeObjectRef(env, out, groups[i]);
589            }
590
591            jvmtiDeallocate(groups);
592        }
593
594    } END_WITH_LOCAL_REFS(env);
595
596    return JNI_TRUE;
597}
598
599static jboolean
600dispose(PacketInputStream *in, PacketOutputStream *out)
601{
602    return JNI_TRUE;
603}
604
605static jboolean
606idSizes(PacketInputStream *in, PacketOutputStream *out)
607{
608    (void)outStream_writeInt(out, sizeof(jfieldID));    /* fields */
609    (void)outStream_writeInt(out, sizeof(jmethodID));   /* methods */
610    (void)outStream_writeInt(out, sizeof(jlong));       /* objects */
611    (void)outStream_writeInt(out, sizeof(jlong));       /* referent types */
612    (void)outStream_writeInt(out, sizeof(FrameID));    /* frames */
613    return JNI_TRUE;
614}
615
616static jboolean
617suspend(PacketInputStream *in, PacketOutputStream *out)
618{
619    jvmtiError error;
620
621    if (gdata->vmDead) {
622        outStream_setError(out, JDWP_ERROR(VM_DEAD));
623        return JNI_TRUE;
624    }
625    error = threadControl_suspendAll();
626    if (error != JVMTI_ERROR_NONE) {
627        outStream_setError(out, map2jdwpError(error));
628    }
629    return JNI_TRUE;
630}
631
632static jboolean
633resume(PacketInputStream *in, PacketOutputStream *out)
634{
635    jvmtiError error;
636
637    if (gdata->vmDead) {
638        outStream_setError(out, JDWP_ERROR(VM_DEAD));
639        return JNI_TRUE;
640    }
641    error = threadControl_resumeAll();
642    if (error != JVMTI_ERROR_NONE) {
643        outStream_setError(out, map2jdwpError(error));
644    }
645    return JNI_TRUE;
646}
647
648static jboolean
649doExit(PacketInputStream *in, PacketOutputStream *out)
650{
651    jint exitCode;
652
653    exitCode = inStream_readInt(in);
654    if (gdata->vmDead) {
655        /* quietly ignore */
656        return JNI_FALSE;
657    }
658
659    /* We send the reply from here because we are about to exit. */
660    if (inStream_error(in)) {
661        outStream_setError(out, inStream_error(in));
662    }
663    outStream_sendReply(out);
664
665    forceExit(exitCode);
666
667    /* Shouldn't get here */
668    JDI_ASSERT(JNI_FALSE);
669
670    /* Shut up the compiler */
671    return JNI_FALSE;
672
673}
674
675static jboolean
676createString(PacketInputStream *in, PacketOutputStream *out)
677{
678    JNIEnv *env;
679    char *cstring;
680
681    if (gdata->vmDead) {
682        outStream_setError(out, JDWP_ERROR(VM_DEAD));
683        return JNI_TRUE;
684    }
685
686    cstring = inStream_readString(in);
687    if (cstring == NULL) {
688        outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
689        return JNI_TRUE;
690    }
691    if (inStream_error(in)) {
692        return JNI_TRUE;
693    }
694
695    env = getEnv();
696
697    WITH_LOCAL_REFS(env, 1) {
698
699        jstring string;
700
701        string = JNI_FUNC_PTR(env,NewStringUTF)(env, cstring);
702        if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
703            outStream_setError(out, JDWP_ERROR(OUT_OF_MEMORY));
704        } else {
705            (void)outStream_writeObjectRef(env, out, string);
706        }
707
708    } END_WITH_LOCAL_REFS(env);
709
710    jvmtiDeallocate(cstring);
711
712    return JNI_TRUE;
713}
714
715static jboolean
716capabilities(PacketInputStream *in, PacketOutputStream *out)
717{
718    jvmtiCapabilities caps;
719    jvmtiError error;
720
721    if (gdata->vmDead) {
722        outStream_setError(out, JDWP_ERROR(VM_DEAD));
723        return JNI_TRUE;
724    }
725    error = jvmtiGetCapabilities(&caps);
726    if (error != JVMTI_ERROR_NONE) {
727        outStream_setError(out, map2jdwpError(error));
728        return JNI_TRUE;
729    }
730
731    (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_modification_events);
732    (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_access_events);
733    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_bytecodes);
734    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_synthetic_attribute);
735    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_info);
736    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_current_contended_monitor);
737    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_monitor_info);
738    return JNI_TRUE;
739}
740
741static jboolean
742capabilitiesNew(PacketInputStream *in, PacketOutputStream *out)
743{
744    jvmtiCapabilities caps;
745    jvmtiError error;
746
747    if (gdata->vmDead) {
748        outStream_setError(out, JDWP_ERROR(VM_DEAD));
749        return JNI_TRUE;
750    }
751    error = jvmtiGetCapabilities(&caps);
752    if (error != JVMTI_ERROR_NONE) {
753        outStream_setError(out, map2jdwpError(error));
754        return JNI_TRUE;
755    }
756
757    (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_modification_events);
758    (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_field_access_events);
759    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_bytecodes);
760    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_synthetic_attribute);
761    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_info);
762    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_current_contended_monitor);
763    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_monitor_info);
764
765    /* new since JDWP version 1.4 */
766    (void)outStream_writeBoolean(out, (jboolean)caps.can_redefine_classes);
767    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE /* can_add_method */ );
768    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE /* can_unrestrictedly_redefine_classes */ );
769    /* 11: canPopFrames */
770    (void)outStream_writeBoolean(out, (jboolean)caps.can_pop_frame);
771    /* 12: canUseInstanceFilters */
772    (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE);
773    /* 13: canGetSourceDebugExtension */
774    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_source_debug_extension);
775    /* 14: canRequestVMDeathEvent */
776    (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE);
777    /* 15: canSetDefaultStratum */
778    (void)outStream_writeBoolean(out, (jboolean)JNI_TRUE);
779    /* 16: canGetInstanceInfo */
780    (void)outStream_writeBoolean(out, (jboolean)caps.can_tag_objects);
781    /* 17: canRequestMonitorEvents */
782    (void)outStream_writeBoolean(out, (jboolean)caps.can_generate_monitor_events);
783    /* 18: canGetMonitorFrameInfo */
784    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_owned_monitor_stack_depth_info);
785    /* remaining reserved */
786    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 19 */
787    /* 20 Can get constant pool information */
788    (void)outStream_writeBoolean(out, (jboolean)caps.can_get_constant_pool);
789    /* 21 Can force early return */
790    (void)outStream_writeBoolean(out, (jboolean)caps.can_force_early_return);
791
792    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 22 */
793    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 23 */
794    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 24 */
795    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 25 */
796    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 26 */
797    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 27 */
798    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 28 */
799    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 29 */
800    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 30 */
801    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 31 */
802    (void)outStream_writeBoolean(out, (jboolean)JNI_FALSE); /* 32 */
803    return JNI_TRUE;
804}
805
806static int
807countPaths(char *string) {
808    int cnt = 1; /* always have one */
809    char *pos = string;
810    char *ps;
811
812    ps = gdata->property_path_separator;
813    if ( ps == NULL ) {
814        ps = ";";
815    }
816    while ((pos = strchr(pos, ps[0])) != NULL) {
817        ++cnt;
818        ++pos;
819    }
820    return cnt;
821}
822
823static void
824writePaths(PacketOutputStream *out, char *string) {
825    char *pos;
826    char *ps;
827    char *buf;
828    int   npaths;
829    int   i;
830
831    buf = jvmtiAllocate((int)strlen(string)+1);
832
833    npaths = countPaths(string);
834    (void)outStream_writeInt(out, npaths);
835
836    ps = gdata->property_path_separator;
837    if ( ps == NULL ) {
838        ps = ";";
839    }
840
841    pos = string;
842    for ( i = 0 ; i < npaths ; i++ ) {
843        char *psPos;
844        int   plen;
845
846        psPos = strchr(pos, ps[0]);
847        if ( psPos == NULL ) {
848            plen = (int)strlen(pos);
849        } else {
850            plen = (int)(psPos-pos);
851            psPos++;
852        }
853        (void)memcpy(buf, pos, plen);
854        buf[plen] = 0;
855        (void)outStream_writeString(out, buf);
856        pos = psPos;
857    }
858
859    jvmtiDeallocate(buf);
860}
861
862
863
864static jboolean
865classPaths(PacketInputStream *in, PacketOutputStream *out)
866{
867    char *ud;
868    char *cp;
869
870    ud = gdata->property_user_dir;
871    if ( ud == NULL ) {
872        ud = "";
873    }
874    cp = gdata->property_java_class_path;
875    if ( cp == NULL ) {
876        cp = "";
877    }
878    (void)outStream_writeString(out, ud);
879    writePaths(out, cp);
880    (void)outStream_writeInt(out, 0); // no bootclasspath
881    return JNI_TRUE;
882}
883
884static jboolean
885disposeObjects(PacketInputStream *in, PacketOutputStream *out)
886{
887    int i;
888    int refCount;
889    jlong id;
890    int requestCount;
891    JNIEnv *env;
892
893    if (gdata->vmDead) {
894        /* quietly ignore */
895        return JNI_TRUE;
896    }
897
898    requestCount = inStream_readInt(in);
899    if (inStream_error(in)) {
900        return JNI_TRUE;
901    }
902
903    env = getEnv();
904    for (i = 0; i < requestCount; i++) {
905        id = inStream_readObjectID(in);
906        refCount = inStream_readInt(in);
907        if (inStream_error(in)) {
908            return JNI_TRUE;
909        }
910        commonRef_releaseMultiple(env, id, refCount);
911    }
912
913    return JNI_TRUE;
914}
915
916static jboolean
917holdEvents(PacketInputStream *in, PacketOutputStream *out)
918{
919    eventHelper_holdEvents();
920    return JNI_TRUE;
921}
922
923static jboolean
924releaseEvents(PacketInputStream *in, PacketOutputStream *out)
925{
926    eventHelper_releaseEvents();
927    return JNI_TRUE;
928}
929
930void *VirtualMachine_Cmds[] = { (void *)22
931    ,(void *)version
932    ,(void *)classesForSignature
933    ,(void *)allClasses
934    ,(void *)getAllThreads
935    ,(void *)topLevelThreadGroups
936    ,(void *)dispose
937    ,(void *)idSizes
938    ,(void *)suspend
939    ,(void *)resume
940    ,(void *)doExit
941    ,(void *)createString
942    ,(void *)capabilities
943    ,(void *)classPaths
944    ,(void *)disposeObjects
945    ,(void *)holdEvents
946    ,(void *)releaseEvents
947    ,(void *)capabilitiesNew
948    ,(void *)redefineClasses
949    ,(void *)setDefaultStratum
950    ,(void *)allClassesWithGeneric
951    ,(void *)instanceCounts
952    ,(void *)allModules
953};
954