| /* |
| * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include "util.h" |
| #include "ReferenceTypeImpl.h" |
| #include "inStream.h" |
| #include "outStream.h" |
| |
| |
| static jboolean |
| signature(PacketInputStream *in, PacketOutputStream *out) |
| { |
| char *signature = NULL; |
| jclass clazz; |
| jvmtiError error; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = classSignature(clazz, &signature, NULL); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeString(out, signature); |
| jvmtiDeallocate(signature); |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| signatureWithGeneric(PacketInputStream *in, PacketOutputStream *out) |
| { |
| /* Returns both the signature and the generic signature */ |
| char *signature = NULL; |
| char *genericSignature = NULL; |
| jclass clazz; |
| jvmtiError error; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| error = classSignature(clazz, &signature, &genericSignature); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeString(out, signature); |
| writeGenericSignature(out, genericSignature); |
| jvmtiDeallocate(signature); |
| if (genericSignature != NULL) { |
| jvmtiDeallocate(genericSignature); |
| } |
| |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| getClassLoader(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jclass clazz; |
| jobject loader; |
| jvmtiError error; |
| JNIEnv *env; |
| |
| env = getEnv(); |
| |
| clazz = inStream_readClassRef(env, in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = classLoader(clazz, &loader); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeObjectRef(env, out, loader); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| getModule(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jobject clazz; |
| jobject module; |
| JNIEnv *env; |
| |
| env = getEnv(); |
| |
| clazz = inStream_readClassRef(env, in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| module = JNI_FUNC_PTR(env, GetModule)(env, clazz); |
| |
| (void)outStream_writeModuleRef(env, out, module); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| modifiers(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jint modifiers; |
| jclass clazz; |
| jvmtiError error; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassModifiers) |
| (gdata->jvmti, clazz, &modifiers); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeInt(out, modifiers); |
| |
| return JNI_TRUE; |
| } |
| |
| static void |
| writeMethodInfo(PacketOutputStream *out, jclass clazz, jmethodID method, |
| int outputGenerics) |
| { |
| char *name = NULL; |
| char *signature = NULL; |
| char *genericSignature = NULL; |
| jint modifiers; |
| jvmtiError error; |
| jboolean isSynthetic; |
| |
| error = isMethodSynthetic(method, &isSynthetic); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| |
| error = methodModifiers(method, &modifiers); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| |
| error = methodSignature(method, &name, &signature, &genericSignature); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| |
| if (isSynthetic) { |
| modifiers |= MOD_SYNTHETIC; |
| } |
| (void)outStream_writeMethodID(out, method); |
| (void)outStream_writeString(out, name); |
| (void)outStream_writeString(out, signature); |
| if (outputGenerics == 1) { |
| writeGenericSignature(out, genericSignature); |
| } |
| (void)outStream_writeInt(out, modifiers); |
| jvmtiDeallocate(name); |
| jvmtiDeallocate(signature); |
| if (genericSignature != NULL) { |
| jvmtiDeallocate(genericSignature); |
| } |
| } |
| |
| static jboolean |
| methods1(PacketInputStream *in, PacketOutputStream *out, |
| int outputGenerics) |
| { |
| int i; |
| jclass clazz; |
| jint methodCount = 0; |
| jmethodID *methods = NULL; |
| jvmtiError error; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassMethods) |
| (gdata->jvmti, clazz, &methodCount, &methods); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeInt(out, methodCount); |
| for (i = 0; (i < methodCount) && !outStream_error(out); i++) { |
| writeMethodInfo(out, clazz, methods[i], outputGenerics); |
| } |
| |
| /* Free methods array */ |
| if ( methods != NULL ) { |
| jvmtiDeallocate(methods); |
| } |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| methods(PacketInputStream *in, PacketOutputStream *out, |
| int outputGenerics) |
| { |
| return methods1(in, out, 0); |
| } |
| |
| static jboolean |
| methodsWithGeneric(PacketInputStream *in, PacketOutputStream *out) |
| { |
| return methods1(in, out, 1); |
| } |
| |
| |
| |
| static jboolean |
| instances(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jint maxInstances; |
| jclass clazz; |
| JNIEnv *env; |
| |
| if (gdata->vmDead) { |
| outStream_setError(out, JDWP_ERROR(VM_DEAD)); |
| return JNI_TRUE; |
| } |
| |
| env = getEnv(); |
| clazz = inStream_readClassRef(env, in); |
| maxInstances = inStream_readInt(in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| WITH_LOCAL_REFS(env, 1) { |
| jvmtiError error; |
| ObjectBatch batch; |
| |
| error = classInstances(clazz, &batch, maxInstances); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| } else { |
| int kk; |
| jbyte typeKey; |
| |
| (void)outStream_writeInt(out, batch.count); |
| if (batch.count > 0) { |
| /* |
| * They are all instances of this class and will all have |
| * the same typeKey, so just compute it once. |
| */ |
| typeKey = specificTypeKey(env, batch.objects[0]); |
| |
| for (kk = 0; kk < batch.count; kk++) { |
| jobject inst; |
| |
| inst = batch.objects[kk]; |
| (void)outStream_writeByte(out, typeKey); |
| (void)outStream_writeObjectRef(env, out, inst); |
| } |
| } |
| jvmtiDeallocate(batch.objects); |
| } |
| } END_WITH_LOCAL_REFS(env); |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| getClassVersion(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jclass clazz; |
| jvmtiError error; |
| jint majorVersion; |
| jint minorVersion; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti, GetClassVersionNumbers) |
| (gdata->jvmti, clazz, &minorVersion, &majorVersion); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeInt(out, majorVersion); |
| (void)outStream_writeInt(out, minorVersion); |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| getConstantPool(PacketInputStream *in, PacketOutputStream *out) |
| { |
| |
| jclass clazz; |
| jvmtiError error; |
| jint cpCount; |
| jint cpByteCount; |
| unsigned char* cpBytesPtr; |
| |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| /* Initialize assuming no bytecodes and no error */ |
| error = JVMTI_ERROR_NONE; |
| cpCount = 0; |
| cpByteCount = 0; |
| cpBytesPtr = NULL; |
| |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetConstantPool) |
| (gdata->jvmti, clazz, &cpCount, &cpByteCount, &cpBytesPtr); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| } else { |
| (void)outStream_writeInt(out, cpCount); |
| (void)outStream_writeByteArray(out, cpByteCount, (jbyte *)cpBytesPtr); |
| jvmtiDeallocate(cpBytesPtr); |
| } |
| |
| return JNI_TRUE; |
| } |
| |
| static void |
| writeFieldInfo(PacketOutputStream *out, jclass clazz, jfieldID fieldID, |
| int outputGenerics) |
| { |
| char *name; |
| char *signature = NULL; |
| char *genericSignature = NULL; |
| jint modifiers; |
| jboolean isSynthetic; |
| jvmtiError error; |
| |
| error = isFieldSynthetic(clazz, fieldID, &isSynthetic); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| |
| error = fieldModifiers(clazz, fieldID, &modifiers); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| |
| error = fieldSignature(clazz, fieldID, &name, &signature, &genericSignature); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return; |
| } |
| if (isSynthetic) { |
| modifiers |= MOD_SYNTHETIC; |
| } |
| (void)outStream_writeFieldID(out, fieldID); |
| (void)outStream_writeString(out, name); |
| (void)outStream_writeString(out, signature); |
| if (outputGenerics == 1) { |
| writeGenericSignature(out, genericSignature); |
| } |
| (void)outStream_writeInt(out, modifiers); |
| jvmtiDeallocate(name); |
| jvmtiDeallocate(signature); |
| if (genericSignature != NULL) { |
| jvmtiDeallocate(genericSignature); |
| } |
| } |
| |
| static jboolean |
| fields1(PacketInputStream *in, PacketOutputStream *out, int outputGenerics) |
| { |
| int i; |
| jclass clazz; |
| jint fieldCount = 0; |
| jfieldID *fields = NULL; |
| jvmtiError error; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetClassFields) |
| (gdata->jvmti, clazz, &fieldCount, &fields); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeInt(out, fieldCount); |
| for (i = 0; (i < fieldCount) && !outStream_error(out); i++) { |
| writeFieldInfo(out, clazz, fields[i], outputGenerics); |
| } |
| |
| /* Free fields array */ |
| if ( fields != NULL ) { |
| jvmtiDeallocate(fields); |
| } |
| return JNI_TRUE; |
| } |
| |
| |
| static jboolean |
| fields(PacketInputStream *in, PacketOutputStream *out) |
| { |
| return fields1(in, out, 0); |
| } |
| |
| static jboolean |
| fieldsWithGeneric(PacketInputStream *in, PacketOutputStream *out) |
| { |
| return fields1(in, out, 1); |
| |
| } |
| |
| static jboolean |
| getValues(PacketInputStream *in, PacketOutputStream *out) |
| { |
| sharedGetFieldValues(in, out, JNI_TRUE); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| sourceFile(PacketInputStream *in, PacketOutputStream *out) |
| { |
| char *fileName; |
| jvmtiError error; |
| jclass clazz; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName) |
| (gdata->jvmti, clazz, &fileName); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeString(out, fileName); |
| jvmtiDeallocate(fileName); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| sourceDebugExtension(PacketInputStream *in, PacketOutputStream *out) |
| { |
| char *extension; |
| jvmtiError error; |
| jclass clazz; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| error = getSourceDebugExtension(clazz, &extension); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| return JNI_TRUE; |
| } |
| |
| (void)outStream_writeString(out, extension); |
| jvmtiDeallocate(extension); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| nestedTypes(PacketInputStream *in, PacketOutputStream *out) |
| { |
| JNIEnv *env; |
| jclass clazz; |
| |
| env = getEnv(); |
| |
| clazz = inStream_readClassRef(env, in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| WITH_LOCAL_REFS(env, 1) { |
| |
| jvmtiError error; |
| jint count; |
| jclass *nested; |
| |
| error = allNestedClasses(clazz, &nested, &count); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| } else { |
| int i; |
| (void)outStream_writeInt(out, count); |
| for (i = 0; i < count; i++) { |
| (void)outStream_writeByte(out, referenceTypeTag(nested[i])); |
| (void)outStream_writeObjectRef(env, out, nested[i]); |
| } |
| if ( nested != NULL ) { |
| jvmtiDeallocate(nested); |
| } |
| } |
| |
| } END_WITH_LOCAL_REFS(env); |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| getClassStatus(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jint status; |
| jclass clazz; |
| |
| clazz = inStream_readClassRef(getEnv(), in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| status = classStatus(clazz); |
| (void)outStream_writeInt(out, map2jdwpClassStatus(status)); |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| interfaces(PacketInputStream *in, PacketOutputStream *out) |
| { |
| JNIEnv *env; |
| jclass clazz; |
| |
| env = getEnv(); |
| |
| clazz = inStream_readClassRef(env, in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| WITH_LOCAL_REFS(env, 1) { |
| |
| jvmtiError error; |
| jint interfaceCount; |
| jclass *interfaces; |
| |
| error = allInterfaces(clazz, &interfaces, &interfaceCount); |
| if (error != JVMTI_ERROR_NONE) { |
| outStream_setError(out, map2jdwpError(error)); |
| } else { |
| int i; |
| |
| (void)outStream_writeInt(out, interfaceCount); |
| for (i = 0; i < interfaceCount; i++) { |
| (void)outStream_writeObjectRef(env, out, interfaces[i]); |
| } |
| if ( interfaces != NULL ) { |
| jvmtiDeallocate(interfaces); |
| } |
| } |
| |
| } END_WITH_LOCAL_REFS(env); |
| |
| return JNI_TRUE; |
| } |
| |
| static jboolean |
| classObject(PacketInputStream *in, PacketOutputStream *out) |
| { |
| jclass clazz; |
| JNIEnv *env; |
| |
| env = getEnv(); |
| clazz = inStream_readClassRef(env, in); |
| if (inStream_error(in)) { |
| return JNI_TRUE; |
| } |
| |
| /* |
| * In our implementation, the reference type id is the same as the |
| * class object id, so we bounce it right back. |
| * |
| */ |
| |
| (void)outStream_writeObjectRef(env, out, clazz); |
| |
| return JNI_TRUE; |
| } |
| |
| void *ReferenceType_Cmds[] = { (void *)19 |
| ,(void *)signature |
| ,(void *)getClassLoader |
| ,(void *)modifiers |
| ,(void *)fields |
| ,(void *)methods |
| ,(void *)getValues |
| ,(void *)sourceFile |
| ,(void *)nestedTypes |
| ,(void *)getClassStatus |
| ,(void *)interfaces |
| ,(void *)classObject |
| ,(void *)sourceDebugExtension |
| ,(void *)signatureWithGeneric |
| ,(void *)fieldsWithGeneric |
| ,(void *)methodsWithGeneric |
| ,(void *)instances |
| ,(void *)getClassVersion |
| ,(void *)getConstantPool |
| ,(void *)getModule |
| }; |