/*
* Copyright (c) 2012, 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.
*
* 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.
*/
package com.sun.max.vm.ext.jvmti;
import static com.sun.max.vm.ext.jvmti.JVMTI.*;
import static com.sun.max.vm.ext.jvmti.JVMTICallbacks.*;
import static com.sun.max.vm.ext.jvmti.JVMTIConstants.*;
import static com.sun.max.vm.ext.jvmti.JVMTIUtil.*;
import java.io.*;
import java.security.*;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.unsafe.*;
import com.sun.max.util.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.classfile.constant.*;
import com.sun.max.vm.ext.jvmti.JJVMTI.*;
import com.sun.max.vm.jni.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.type.*;
/**
* Support for all the JVMTI functions related to {@link Class} handling.
*/
class JVMTIClassFunctions {
/**
* Strict check on whether a class is a VM class.
* @param classActor
*/
static boolean isVMClass(ClassActor classActor) {
return classActor.classLoader == VMClassLoader.VM_CLASS_LOADER;
}
/**
* Non-strict check whether a class is a VM class.
*
* @param classActor
* @return If {@link JVMTI#JVMTI_VM} is {@code false} equivalent to {@code !isVMClass(classActor), otherwise {@link true}.
*/
static boolean isVisibleClass(ClassActor classActor) {
return JVMTI.JVMTI_VM || !isVMClass(classActor);
}
static int getObjectSize(Object object, Pointer sizePtr) {
sizePtr.setInt(Layout.size(Reference.fromJava(object)).toInt());
return JVMTI_ERROR_NONE;
}
/**
* Dispatch the {@link JVMTIEvents#CLASS_FILE_LOAD_HOOK}.
* We do not check the event state, caller is presumed to have called {@link JVMTI#eventNeeded(int).
* @param classLoader null for boot
* @param className
* @param protectionDomain
* @param classfileBytes
* @return transformed bytes or null if no change
*/
static byte[] classFileLoadHook(ClassLoader classLoader, String className, ProtectionDomain protectionDomain, byte[] classfileBytes) {
Pointer newClassDataLenPtr = Intrinsics.alloca(Pointer.size(), false);
Pointer newClassDataPtrPtr = Intrinsics.alloca(Pointer.size(), false);
Pointer classDataPtr = Reference.fromJava(classfileBytes).toOrigin().plus(JVMTIUtil.byteDataOffset);
int classfileBytesLength = classfileBytes.length;
boolean changed = false;
for (int i = 0; i < MAX_NATIVE_ENVS; i++) {
NativeEnv nativEnv = (NativeEnv) jvmtiEnvs[i];
Pointer callback = getCallbackForEvent(nativEnv, JVMTIEvents.E.CLASS_FILE_LOAD_HOOK, VmThread.current());
if (callback.isZero()) {
continue;
}
Pointer cstruct = nativEnv.cstruct;
// class name is passed as a char * not a JNI handle, sigh
Pointer classNamePtr = Pointer.zero();
if (className != null) {
classNamePtr = CString.utf8FromJava(className);
}
newClassDataPtrPtr.setWord(Word.zero());
invokeClassfileLoadHookCallback(
callback, cstruct, Word.zero(),
JniHandles.createLocalHandle(classLoader), classNamePtr, JniHandles.createLocalHandle(protectionDomain), classfileBytesLength,
classDataPtr, newClassDataLenPtr, newClassDataPtrPtr);
if (!classNamePtr.isZero()) {
Memory.deallocate(classNamePtr);
}
if (!newClassDataPtrPtr.getWord().isZero()) {
classDataPtr = newClassDataPtrPtr.getWord().asPointer();
classfileBytesLength = newClassDataLenPtr.getInt();
changed = true;
}
}
if (changed) {
classfileBytes = new byte[classfileBytesLength];
Memory.readBytes(classDataPtr, classfileBytes);
}
// now the Java agents
for (int i = MAX_NATIVE_ENVS; i < MAX_ENVS; i++) {
JavaEnv javaEnv = (JavaEnv) jvmtiEnvs[i];
if (javaEnv == null || !JVMTIEvents.isEventSet(javaEnv, JVMTIEvents.E.CLASS_FILE_LOAD_HOOK, VmThread.current())) {
continue;
}
byte[] newClassfileBytes = javaEnv.callbackHandler.classFileLoadHook(classLoader, className, protectionDomain, classfileBytes);
if (newClassfileBytes != null) {
classfileBytes = newClassfileBytes;
changed = true;
}
}
if (changed) {
return classfileBytes;
} else {
return null;
}
}
/**
* Add a directory/jar to the boot classpath. Only one addition per agent is supported. Since this if typically
* called in the ONLOAD phase we hold the value until Maxine has reached PRISTINE.
*/
static int addToBootstrapClassLoaderSearch(Pointer env, Pointer segment) {
// TODO Handle the case where paths are added after getAddedBootClassPath has been called,
// i.e, during the LIVE phase
if (getAddedBootClassPathCalled) {
return JVMTI_ERROR_INTERNAL;
}
NativeEnv jvmtiEnv = (NativeEnv) JVMTI.getEnv(env);
if (jvmtiEnv == null) {
return JVMTI_ERROR_INVALID_ENVIRONMENT;
}
for (int i = 0; i < jvmtiEnv.bootClassPathAdd.length; i++) {
long p = jvmtiEnv.bootClassPathAdd[i];
if (p == 0) {
Size length = CString.length(segment).plus(1);
Pointer copiedPath = Memory.allocate(length);
Memory.copyBytes(segment, copiedPath, length);
jvmtiEnv.bootClassPathAdd[i] = copiedPath.asAddress().toLong();
return JVMTI_ERROR_NONE;
}
}
return JVMTI_ERROR_INTERNAL;
}
private static boolean getAddedBootClassPathCalled;
/**
* Handles any extra paths added in the ONLOAD phase.
*
*/
static String getAddedBootClassPath() {
if (JVMTI.activeEnvCount == 0) {
return null;
}
String result = null;
for (int i = 0; i < MAX_NATIVE_ENVS; i++) {
NativeEnv jvmtiEnv = (NativeEnv) jvmtiEnvs[i];
for (long pathAsLong : jvmtiEnv.bootClassPathAdd) {
if (pathAsLong != 0) {
Pointer pathPtr = Address.fromLong(pathAsLong).asPointer();
try {
String path = CString.utf8ToJava(pathPtr);
if (result == null) {
result = path;
} else {
result = result + File.pathSeparator + path;
}
} catch (Utf8Exception ex) {
// skip it
}
}
}
}
getAddedBootClassPathCalled = true;
return result;
}
static int getClassSignature(Class klass, Pointer signaturePtrPtr, Pointer genericPtrPtr) {
ClassActor classActor = ClassActor.fromJava(klass);
if (!signaturePtrPtr.isZero()) {
Pointer signaturePtr = CString.utf8FromJava(classActor.typeDescriptor.string);
if (signaturePtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
signaturePtrPtr.setWord(signaturePtr);
}
if (!genericPtrPtr.isZero()) {
genericPtrPtr.setWord(Pointer.zero());
}
return JVMTI_ERROR_NONE;
}
static int getClassStatus(ClassActor classActor) {
int status = 0;
if (classActor.isArrayClass()) {
status = JVMTI_CLASS_STATUS_ARRAY;
} else if (classActor.isPrimitiveClassActor()) {
status = JVMTI_CLASS_STATUS_PRIMITIVE;
} else {
// ClassActor keeps a single state for the class, so we have to work backwards.
ClassActorProxy proxyClassActor = ClassActorProxy.asClassActorProxy(classActor);
if (proxyClassActor.initializationState == ClassActorProxy.INITIALIZED) {
status = JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED | JVMTI_CLASS_STATUS_INITIALIZED;
} else if (proxyClassActor.initializationState == ClassActorProxy.VERIFIED_) {
status = JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED;
} else if (proxyClassActor.initializationState == ClassActorProxy.PREPARED) {
status = JVMTI_CLASS_STATUS_PREPARED;
} else {
// any other value implies some kind of error
status = JVMTI_CLASS_STATUS_ERROR;
}
}
return status;
}
static int getClassStatus(Class<?> klass) {
return getClassStatus(ClassActor.fromJava(klass));
}
static int getClassStatus(Class<?> klass, Pointer statusPtr) {
statusPtr.setInt(getClassStatus(klass));
return JVMTI_ERROR_NONE;
}
static int getByteCodes(ClassMethodActor classMethodActor, Pointer bytecodeCountPtr, Pointer bytecodesPtr) {
byte[] code = classMethodActor.code();
bytecodeCountPtr.setInt(code.length);
Pointer nativeBytesPtr = Memory.allocate(Size.fromInt(code.length));
Memory.writeBytes(code, nativeBytesPtr);
bytecodesPtr.setWord(nativeBytesPtr);
return JVMTI_ERROR_NONE;
}
/**
* Union class to handle native/java variants for {@link #getLoadedClasses} etc.
*/
private static class LoadedClassesUnion extends ModeUnion {
ClassActor[] classActorArray;
Pointer classesArrayPtr;
int classCount;
LoadedClassesUnion(boolean isNative) {
super(isNative);
}
}
/**
* Get the classes whose loading was initiated by the given class loader. TODO: Handle initiating versus defining
* loaders (which requires a Maxine upgrade).
*/
static ClassActor[] getClassLoaderClasses(ClassLoader classLoader) {
LoadedClassesUnion lcu = new LoadedClassesUnion(false);
getClassLoaderClasses(lcu, classLoader);
return lcu.classActorArray;
}
static int getClassLoaderClasses(ClassLoader classLoader, Pointer classCountPtr, Pointer classesPtrPtr) {
LoadedClassesUnion lcu = new LoadedClassesUnion(true);
int error = getClassLoaderClasses(lcu, classLoader);
if (error == JVMTI_ERROR_NONE) {
classCountPtr.setInt(lcu.classCount);
classesPtrPtr.setWord(lcu.classesArrayPtr);
}
return error;
}
static int getClassLoaderClasses(LoadedClassesUnion lcu, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = BootClassLoader.BOOT_CLASS_LOADER;
}
Collection<ClassActor> classActors = ClassRegistry.makeRegistry(classLoader).getClassActors();
int classCount = classActors.size();
if (lcu.isNative) {
lcu.classesArrayPtr = allocateClassesArray(classCount);
if (lcu.classesArrayPtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
} else {
lcu.classActorArray = new ClassActor[classCount];
}
copyClassActors(lcu, classActors, 0, classCount);
lcu.classCount = classCount;
return JVMTI_ERROR_NONE;
}
private static final int NUMBER_OF_PRIMITIVE_CLASS_ACTORS = 9; // void,byte,boolean,short,char,int,long,float,double
static int getLoadedClasses(Pointer classCountPtr, Pointer classesPtrPtr) {
LoadedClassesUnion lcu = new LoadedClassesUnion(true);
int error = getLoadedClasses(lcu);
if (error == JVMTI_ERROR_NONE) {
classCountPtr.setInt(lcu.classCount);
classesPtrPtr.setWord(lcu.classesArrayPtr);
}
return error;
}
static ClassActor[] getLoadedClassActors() {
LoadedClassesUnion lcu = new LoadedClassesUnion(false);
getLoadedClasses(lcu);
return lcu.classActorArray;
}
private static int getLoadedClasses(LoadedClassesUnion lcu) {
// TODO handle all class loaders, requires changes to Maxine
Collection<ClassActor> bootClassActors = ClassRegistry.makeRegistry(BootClassLoader.BOOT_CLASS_LOADER).getClassActors();
Collection<ClassActor> systemClassActors = ClassRegistry.makeRegistry(ClassLoader.getSystemClassLoader()).getClassActors();
@SuppressWarnings("unchecked")
Collection<ClassActor> vmClassActors = JVMTI.JVMTI_VM ? ClassRegistry.makeRegistry(VMClassLoader.VM_CLASS_LOADER).getClassActors() : Collections.EMPTY_SET;
// N.B. This is inherently a snapshot as we don't prevent class loading/unloading happening while this executes.
// These variables define the snapshot, and copyClassActors honors these values for recording purposes.
// Another complication is that spec sates that primitive class actors are not returned
// and Maxine's boot class registry does include those.
int bootClassActorsSize = bootClassActors.size() - NUMBER_OF_PRIMITIVE_CLASS_ACTORS;
int systemClassActorsSize = systemClassActors.size();
int totalSize = bootClassActorsSize + systemClassActorsSize + vmClassActors.size();
if (lcu.isNative) {
lcu.classesArrayPtr = allocateClassesArray(totalSize);
if (lcu.classesArrayPtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
} else {
lcu.classActorArray = new ClassActor[totalSize];
}
int bootClassActorsCopied = copyClassActors(lcu, bootClassActors, 0, bootClassActorsSize);
int systemClassActorsCopied = copyClassActors(lcu, systemClassActors, bootClassActorsCopied, systemClassActorsSize);
int vmClassActorsCopied = JVMTI.JVMTI_VM ? copyClassActors(lcu, vmClassActors, systemClassActorsCopied + bootClassActorsCopied, vmClassActors.size()) : 0;
lcu.classCount = bootClassActorsCopied + systemClassActorsCopied + vmClassActorsCopied;
return JVMTI_ERROR_NONE;
}
private static Pointer allocateClassesArray(int classCount) {
Pointer classesPtr = Memory.allocate(Size.fromInt(classCount * Word.size()));
if (classesPtr.isZero()) {
return classesPtr;
}
return classesPtr;
}
/**
* Copies at most {@code count} classes into a C array or Java array. Returns the actual number of
* classes copied. This maybe {@code <= count} the number of class actors has shrunk due to unloading. The added
* classes are stored starting at {@code arrayIndex}.
*/
private static int copyClassActors(LoadedClassesUnion lcu, Collection<ClassActor> classActors, int arrayIndex, int count) {
int index = 0;
for (ClassActor classActor : classActors) {
if (classActor.isPrimitiveClassActor()) {
continue;
}
if (index < count) {
Class<?> klass = classActor.toJava();
if (lcu.isNative) {
lcu.classesArrayPtr.setWord(arrayIndex + index, JniHandles.createLocalHandle(klass));
} else {
lcu.classActorArray[arrayIndex + index] = classActor;
}
index++;
} else {
break;
}
}
return index;
}
static int getSourceFileName(Class klass, Pointer sourceNamePtr) {
ClassActor classActor = ClassActor.fromJava(klass);
String className = classActor.sourceFileName;
Pointer classNamePtr = CString.utf8FromJava(className);
sourceNamePtr.setWord(classNamePtr);
return JVMTI_ERROR_NONE;
}
private static class LineNumberTableUnion extends ModeUnion {
// NATIVE
Pointer nativeTablePtr;
int count;
// JAVA
LineNumberEntry[] lineNumberEntries;
LineNumberTableUnion(boolean isNative) {
super(isNative);
}
}
static LineNumberEntry[] getLineNumberTable(ClassMethodActor method) {
LineNumberTableUnion lnu = new LineNumberTableUnion(ModeUnion.JAVA);
int error = getLineNumberTable(method, lnu);
if (error == JVMTI_ERROR_NONE) {
return lnu.lineNumberEntries;
} else {
throw new JJVMTI.JJVMTIException(error);
}
}
static int getLineNumberTable(ClassMethodActor classMethodActor, Pointer entryCountPtr, Pointer tablePtr) {
LineNumberTableUnion lnu = new LineNumberTableUnion(ModeUnion.NATIVE);
int error = getLineNumberTable(classMethodActor, lnu);
if (error == JVMTI_ERROR_NONE) {
entryCountPtr.setInt(lnu.count);
tablePtr.setWord(lnu.nativeTablePtr);
}
return error;
}
static int getLineNumberTable(ClassMethodActor classMethodActor, LineNumberTableUnion lnu) {
if (classMethodActor.isNative()) {
return JVMTI_ERROR_NATIVE_METHOD;
}
LineNumberTable table = classMethodActor.codeAttribute().lineNumberTable();
if (table.isEmpty()) {
return JVMTI_ERROR_ABSENT_INFORMATION;
}
LineNumberTable.Entry[] entries = table.entries();
lnu.count = entries.length;
if (lnu.isNative) {
lnu.nativeTablePtr = Memory.allocate(Size.fromInt(entries.length * getLineNumberEntrySize()));
} else {
lnu.lineNumberEntries = new LineNumberEntry[entries.length];
}
for (int i = 0; i < entries.length; i++) {
LineNumberTable.Entry entry = entries[i];
if (lnu.isNative) {
setJVMTILineNumberEntry(lnu.nativeTablePtr, i, entry.bci(), entry.lineNumber());
} else {
lnu.lineNumberEntries[i] = new LineNumberEntry(entry.bci(), entry.lineNumber());
}
}
return JVMTI_ERROR_NONE;
}
private static int lineNumberEntrySize = -1;
private static int getLineNumberEntrySize() {
if (lineNumberEntrySize < 0) {
lineNumberEntrySize = getJVMTILineNumberEntrySize();
}
return lineNumberEntrySize;
}
@C_FUNCTION
private static native int getJVMTILineNumberEntrySize();
@C_FUNCTION
private static native void setJVMTILineNumberEntry(Pointer table, int index, long location, int lineNumber);
/**
* To order local variable entries by slot index.
*/
private static class EntryComparator implements Comparator<LocalVariableTable.Entry> {
public int compare(LocalVariableTable.Entry a, LocalVariableTable.Entry b) {
final int aSlot = a.slot();
final int bSlot = b.slot();
if (aSlot < bSlot) {
return -1;
} else if (aSlot > bSlot) {
return 1;
} else {
return 0;
}
}
}
private static final EntryComparator entryComparator = new EntryComparator();
private static class LocalVariableTableUnion extends ModeUnion {
int entryCount;
Pointer nativeTablePtr;
JJVMTI.LocalVariableEntry[] localVariableEntryArray;
LocalVariableTableUnion(boolean isNative) {
super(isNative);
}
}
static int getLocalVariableTable(ClassMethodActor classMethodActor, Pointer entryCountPtr, Pointer tablePtr) {
LocalVariableTableUnion lvtu = new LocalVariableTableUnion(true);
int error = getLocalVariableTable(lvtu, classMethodActor);
if (error == JVMTI_ERROR_NONE) {
entryCountPtr.setInt(lvtu.entryCount);
tablePtr.setWord(lvtu.nativeTablePtr);
}
return error;
}
static JJVMTI.LocalVariableEntry[] getLocalVariableTable(ClassMethodActor classMethodActor) {
LocalVariableTableUnion lvtu = new LocalVariableTableUnion(false);
int error = getLocalVariableTable(lvtu, classMethodActor);
if (error == JVMTI_ERROR_NONE) {
return lvtu.localVariableEntryArray;
} else {
if (error == JVMTI_ERROR_ABSENT_INFORMATION) {
return new JJVMTI.LocalVariableEntry[0];
}
throw new JJVMTI.JJVMTIException(error);
}
}
static int getLocalVariableTable(LocalVariableTableUnion lvtu, ClassMethodActor classMethodActor) {
if (classMethodActor.isNative()) {
return JVMTI_ERROR_NATIVE_METHOD;
}
LocalVariableTable table = classMethodActor.codeAttribute().localVariableTable();
// Maxine does not (currently) distinguish a class file with no LocalVariableTableAttribute
// and one with an empty table.
if (table.isEmpty()) {
return JVMTI_ERROR_ABSENT_INFORMATION;
}
LocalVariableTable.Entry[] entries = table.entries();
// The spec doesn't say anything about ordering but, experimentally, it is important to order by slot
// otherwise debuggers show the arguments to a method in the random order returned by table.entries().
Arrays.sort(entries, entryComparator);
ConstantPool constantPool = classMethodActor.holder().constantPool();
lvtu.entryCount = entries.length;
if (lvtu.isNative) {
lvtu.nativeTablePtr = Memory.allocate(Size.fromInt(entries.length * getLocalVariableEntrySize()));
for (int i = 0; i < entries.length; i++) {
LocalVariableTable.Entry entry = entries[i];
setJVMTILocalVariableEntry(lvtu.nativeTablePtr, i, CString.utf8FromJava(entry.name(constantPool).string),
CString.utf8FromJava(constantPool.utf8At(entry.descriptorIndex(), "local variable type").toString()),
entry.signatureIndex() == 0 ? Pointer.zero() : CString.utf8FromJava(entry.signature(constantPool).string), entry.startBCI(), entry.length(), entry.slot());
}
} else {
lvtu.localVariableEntryArray = new JJVMTI.LocalVariableEntry[entries.length];
for (int i = 0; i < entries.length; i++) {
LocalVariableTable.Entry entry = entries[i];
lvtu.localVariableEntryArray[i] = new JJVMTI.LocalVariableEntry(entry.startBCI(), entry.length(),
entry.name(constantPool).string, constantPool.utf8At(entry.descriptorIndex(), "local variable type").toString(),
entry.signatureIndex() == 0 ? null : entry.signature(constantPool).string, entry.slot());
}
}
return JVMTI_ERROR_NONE;
}
private static int localVariableEntrySize = -1;
private static int getLocalVariableEntrySize() {
if (localVariableEntrySize < 0) {
localVariableEntrySize = getJVMTILocalVariableEntrySize();
}
return localVariableEntrySize;
}
@C_FUNCTION
private static native int getJVMTILocalVariableEntrySize();
@C_FUNCTION
private static native void setJVMTILocalVariableEntry(Pointer table, int index, Pointer name, Pointer signature, Pointer genericSignature, long location, int length, int slot);
static int getSourceDebugExtension(Class klass, Pointer sourceDebugExtensionPtr) {
return JVMTI_ERROR_ABSENT_INFORMATION;
}
static int isMethodObsolete(ClassMethodActor classMethodActor, Pointer isObsoletePtr) {
// TODO implement properly!
isObsoletePtr.setBoolean(false);
return JVMTI_ERROR_NONE;
}
static int getMethodName(MethodActor methodActor, Pointer namePtrPtr, Pointer signaturePtrPtr, Pointer genericPtrPtr) {
if (!namePtrPtr.isZero()) {
namePtrPtr.setWord(CString.utf8FromJava(methodActor.name()));
}
if (!signaturePtrPtr.isZero()) {
signaturePtrPtr.setWord(CString.utf8FromJava(methodActor.descriptor().asString()));
}
if (!genericPtrPtr.isZero()) {
String generic = methodActor.genericSignatureString();
genericPtrPtr.setWord(generic == null ? Pointer.zero() : CString.utf8FromJava(generic));
}
return JVMTI_ERROR_NONE;
}
static int getMethodDeclaringClass(MethodActor methodActor, Pointer declaringClassPtr) {
declaringClassPtr.setWord(JniHandles.createLocalHandle(methodActor.holder().toJava()));
return JVMTI_ERROR_NONE;
}
static int getMaxLocals(ClassMethodActor classMethodActor, Pointer maxPtr) {
if (classMethodActor.isNative()) {
return JVMTI_ERROR_INVALID_METHODID;
}
maxPtr.setInt(classMethodActor.codeAttribute().maxLocals);
return JVMTI_ERROR_NONE;
}
static int getArgumentsSize(ClassMethodActor classMethodActor, Pointer sizePtr) {
if (classMethodActor.isNative()) {
return JVMTI_ERROR_INVALID_METHODID;
}
sizePtr.setInt(classMethodActor.numberOfParameterSlots());
return JVMTI_ERROR_NONE;
}
static int getMethodLocation(ClassMethodActor classMethodActor, Pointer startLocationPtr, Pointer endLocationPtr) {
if (classMethodActor.isNative()) {
return JVMTI_ERROR_INVALID_METHODID;
}
byte[] code = classMethodActor.codeAttribute().code();
startLocationPtr.setLong(0);
endLocationPtr.setLong(code.length - 1);
return JVMTI_ERROR_NONE;
}
static int getFieldName(FieldActor fieldActor, Pointer namePtrPtr, Pointer signaturePtrPtr, Pointer genericPtrPtr) {
if (!namePtrPtr.isZero()) {
namePtrPtr.setWord(CString.utf8FromJava(fieldActor.name()));
}
if (!signaturePtrPtr.isZero()) {
signaturePtrPtr.setWord(CString.utf8FromJava(fieldActor.descriptor().string));
}
if (!genericPtrPtr.isZero()) {
String generic = fieldActor.genericSignatureString();
genericPtrPtr.setWord(generic == null ? Pointer.zero() : CString.utf8FromJava(generic));
}
return JVMTI_ERROR_NONE;
}
static int getFieldDeclaringClass(FieldActor fieldActor, Pointer declaringClassPtr) {
declaringClassPtr.setWord(JniHandles.createLocalHandle(fieldActor.holder().toJava()));
return JVMTI_ERROR_NONE;
}
private static class ClassMethodsUnion extends ModeUnion {
int length;
Pointer methodsPtr;
MethodActor[] methodsArray;
ClassMethodsUnion(boolean isNative) {
super(isNative);
}
}
static MethodActor[] getClassMethods(ClassActor klass) {
ClassMethodsUnion cmu = new ClassMethodsUnion(false);
getClassMethods(klass, cmu);
return cmu.methodsArray;
}
static int getClassMethods(Class klass, Pointer methodCountPtr, Pointer methodsPtrPtr) {
ClassMethodsUnion cmu = new ClassMethodsUnion(true);
int error = getClassMethods(ClassActor.fromJava(klass), cmu);
methodsPtrPtr.setWord(cmu.methodsPtr);
methodCountPtr.setInt(cmu.length);
return error;
}
private static int getClassMethods(ClassActor classActor, ClassMethodsUnion cmu) {
MethodActor[] methodActors = ClassActorProxy.asClassActorProxy(classActor).methodActors;
boolean ordered = methodActors != null;
if (!ordered) {
methodActors = classActor.getLocalMethodActorsArray();
}
int length = methodActors.length;
if (cmu.isNative) {
cmu.methodsPtr = Memory.allocate(Size.fromInt(length * Word.size()));
if (cmu.methodsPtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
} else {
cmu.methodsArray = new MethodActor[length];
}
cmu.length = length;
// One issue: Maxine puts clinit at the end, it should be first (based on Hotspot).
int last = length;
int offset = 0;
if (ordered) {
if (classActor.clinit != null) {
assert methodActors[last - 1] == classActor.clinit;
if (cmu.isNative) {
cmu.methodsPtr.setWord(0, MethodID.fromMethodActor(classActor.clinit));
} else {
cmu.methodsArray[0] = classActor.clinit;
}
last--;
offset++;
}
}
for (int i = 0; i < last; i++) {
if (cmu.isNative) {
cmu.methodsPtr.setWord(i + offset, MethodID.fromMethodActor(methodActors[i]));
} else {
cmu.methodsArray[i + offset] = methodActors[i];
}
}
return JVMTI_ERROR_NONE;
}
static int getClassFields(Class klass, Pointer fieldCountPtr, Pointer fieldsPtrPtr) {
List<FieldActor> fieldActors = ClassActor.fromJava(klass).getLocalFieldActors();
Pointer fieldsPtr = Memory.allocate(Size.fromInt(fieldActors.size() * Word.size()));
if (fieldsPtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
fieldsPtrPtr.setWord(fieldsPtr);
int size = fieldActors.size();
for (int i = 0; i < size; i++) {
fieldsPtr.setWord(i, FieldID.fromFieldActor(fieldActors.get(i)));
}
fieldCountPtr.setInt(size);
return JVMTI_ERROR_NONE;
}
static int getImplementedInterfaces(Class klass, Pointer interfaceCountPtr, Pointer interfacesPtrPtr) {
List<InterfaceActor> interfaceActors = ClassActor.fromJava(klass).getLocalInterfaceActors();
Pointer interfacesPtr = Memory.allocate(Size.fromInt(interfaceActors.size() * Word.size()));
if (interfacesPtr.isZero()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
interfacesPtrPtr.setWord(interfacesPtr);
int size = interfaceActors.size();
for (int i = 0; i < size; i++) {
interfacesPtr.setWord(i, JniHandles.createLocalHandle(interfaceActors.get(i).toJava()));
}
interfaceCountPtr.setInt(size);
return JVMTI_ERROR_NONE;
}
}