1 /*
   2  * Copyright (c) 1998, 2020, 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 "MethodImpl.h"
  28 #include "inStream.h"
  29 #include "outStream.h"
  30 
  31 static jboolean
  32 lineTable(PacketInputStream *in, PacketOutputStream *out)
  33 {
  34     jvmtiError error;
  35     jint count = 0;
  36     jvmtiLineNumberEntry *table = NULL;
  37     jmethodID method;
  38     jlocation firstCodeIndex;
  39     jlocation lastCodeIndex;
  40     jboolean isNative;
  41 
  42     /* JVMDI needed the class, but JVMTI does not so we ignore it */
  43     (void)inStream_readClassRef(getEnv(), in);
  44     if (inStream_error(in)) {
  45         return JNI_TRUE;
  46     }
  47     method = inStream_readMethodID(in);
  48     if (inStream_error(in)) {
  49         return JNI_TRUE;
  50     }
  51 
  52     /*
  53      * JVMTI behavior for the calls below is unspecified for native
  54      * methods, so we must check explicitly.
  55      */
  56     isNative = isMethodNative(method);
  57     if (isNative) {
  58         outStream_setError(out, JDWP_ERROR(NATIVE_METHOD));
  59         return JNI_TRUE;
  60     }
  61 
  62     error = methodLocation(method, &firstCodeIndex, &lastCodeIndex);
  63     if (error != JVMTI_ERROR_NONE) {
  64         outStream_setError(out, map2jdwpError(error));
  65         return JNI_TRUE;
  66     }
  67     (void)outStream_writeLocation(out, firstCodeIndex);
  68     (void)outStream_writeLocation(out, lastCodeIndex);
  69 
  70     error = JVMTI_FUNC_PTR(gdata->jvmti,GetLineNumberTable)
  71                 (gdata->jvmti, method, &count, &table);
  72     if (error == JVMTI_ERROR_ABSENT_INFORMATION) {
  73         /*
  74          * Indicate no line info with an empty table. The code indices
  75          * are still useful, so we don't want to return an error
  76          */
  77         (void)outStream_writeInt(out, 0);
  78     } else if (error == JVMTI_ERROR_NONE) {
  79         jint i;
  80         (void)outStream_writeInt(out, count);
  81         for (i = 0; (i < count) && !outStream_error(out); i++) {
  82             (void)outStream_writeLocation(out, table[i].start_location);
  83             (void)outStream_writeInt(out, table[i].line_number);
  84         }
  85         jvmtiDeallocate(table);
  86     } else {
  87         outStream_setError(out, map2jdwpError(error));
  88     }
  89     return JNI_TRUE;
  90 }
  91 
  92 
  93 static jboolean
  94 doVariableTable(PacketInputStream *in, PacketOutputStream *out,
  95                 int outputGenerics)
  96 {
  97     jvmtiError error;
  98     jint count;
  99     jvmtiLocalVariableEntry *table;
 100     jmethodID method;
 101     jint argsSize;
 102     jboolean isNative;
 103 
 104     /* JVMDI needed the class, but JVMTI does not so we ignore it */
 105     (void)inStream_readClassRef(getEnv(), in);
 106     if (inStream_error(in)) {
 107         return JNI_TRUE;
 108     }
 109     method = inStream_readMethodID(in);
 110     if (inStream_error(in)) {
 111         return JNI_TRUE;
 112     }
 113 
 114     /*
 115      * JVMTI behavior for the calls below is unspecified for native
 116      * methods, so we must check explicitly.
 117      */
 118     isNative = isMethodNative(method);
 119     if (isNative) {
 120         outStream_setError(out, JDWP_ERROR(NATIVE_METHOD));
 121         return JNI_TRUE;
 122     }
 123 
 124     error = JVMTI_FUNC_PTR(gdata->jvmti,GetArgumentsSize)
 125                 (gdata->jvmti, method, &argsSize);
 126     if (error != JVMTI_ERROR_NONE) {
 127         outStream_setError(out, map2jdwpError(error));
 128         return JNI_TRUE;
 129     }
 130 
 131     error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalVariableTable)
 132                 (gdata->jvmti, method, &count, &table);
 133     if (error == JVMTI_ERROR_NONE) {
 134         jint i;
 135         (void)outStream_writeInt(out, argsSize);
 136         (void)outStream_writeInt(out, count);
 137         for (i = 0; (i < count) && !outStream_error(out); i++) {
 138             jvmtiLocalVariableEntry *entry = &table[i];
 139             (void)outStream_writeLocation(out, entry->start_location);
 140             (void)outStream_writeString(out, entry->name);
 141             (void)outStream_writeString(out, entry->signature);
 142             if (outputGenerics == 1) {
 143                 writeGenericSignature(out, entry->generic_signature);
 144             }
 145             (void)outStream_writeInt(out, entry->length);
 146             (void)outStream_writeInt(out, entry->slot);
 147 
 148             jvmtiDeallocate(entry->name);
 149             jvmtiDeallocate(entry->signature);
 150             if (entry->generic_signature != NULL) {
 151               jvmtiDeallocate(entry->generic_signature);
 152             }
 153         }
 154 
 155         jvmtiDeallocate(table);
 156     } else {
 157         outStream_setError(out, map2jdwpError(error));
 158     }
 159     return JNI_TRUE;
 160 }
 161 
 162 
 163 static jboolean
 164 variableTable(PacketInputStream *in, PacketOutputStream *out) {
 165     return doVariableTable(in, out, 0);
 166 }
 167 
 168 static jboolean
 169 variableTableWithGenerics(PacketInputStream *in, PacketOutputStream *out) {
 170     return doVariableTable(in, out, 1);
 171 }
 172 
 173 
 174 static jboolean
 175 bytecodes(PacketInputStream *in, PacketOutputStream *out)
 176 {
 177     jvmtiError error;
 178     unsigned char * bcp;
 179     jint bytecodeCount;
 180     jmethodID method;
 181 
 182     /* JVMDI needed the class, but JVMTI does not so we ignore it */
 183     (void)inStream_readClassRef(getEnv(), in);
 184     if (inStream_error(in)) {
 185         return JNI_TRUE;
 186     }
 187     method = inStream_readMethodID(in);
 188     if (inStream_error(in)) {
 189         return JNI_TRUE;
 190     }
 191 
 192     /* Initialize assuming no bytecodes and no error */
 193     error         = JVMTI_ERROR_NONE;
 194     bytecodeCount = 0;
 195     bcp           = NULL;
 196 
 197     /* Only non-native methods have bytecodes, don't even ask if native. */
 198     if ( !isMethodNative(method) ) {
 199         error = JVMTI_FUNC_PTR(gdata->jvmti,GetBytecodes)
 200                     (gdata->jvmti, method, &bytecodeCount, &bcp);
 201     }
 202     if (error != JVMTI_ERROR_NONE) {
 203         outStream_setError(out, map2jdwpError(error));
 204     } else {
 205         (void)outStream_writeByteArray(out, bytecodeCount, (jbyte *)bcp);
 206         jvmtiDeallocate(bcp);
 207     }
 208 
 209     return JNI_TRUE;
 210 }
 211 
 212 static jboolean
 213 isObsolete(PacketInputStream *in, PacketOutputStream *out)
 214 {
 215     jboolean isObsolete;
 216     jmethodID method;
 217 
 218     /* JVMDI needed the class, but JVMTI does not so we ignore it */
 219     (void)inStream_readClassRef(getEnv(), in);
 220     if (inStream_error(in)) {
 221         return JNI_TRUE;
 222     }
 223     method = inStream_readMethodID(in);
 224     if (inStream_error(in)) {
 225         return JNI_TRUE;
 226     }
 227 
 228     isObsolete = isMethodObsolete(method);
 229     (void)outStream_writeBoolean(out, isObsolete);
 230 
 231     return JNI_TRUE;
 232 }
 233 
 234 CommandSet Method_Cmds = {
 235     5, "Method",
 236     {
 237         {lineTable, "lineTable"},
 238         {variableTable, "variableTable"},
 239         {bytecodes, "bytecodes"},
 240         {isObsolete, "isObsolete"},
 241         {variableTableWithGenerics, "variableTableWithGenerics"}
 242     }
 243 };