blob: c2c7c28e7d1c540b9054875ad2af45287488ba4a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.debugger.jpda.visual;
import com.sun.jdi.ArrayReference;
import com.sun.jdi.ArrayType;
import com.sun.jdi.BooleanValue;
import com.sun.jdi.ByteValue;
import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.ClassObjectReference;
import com.sun.jdi.ClassType;
import com.sun.jdi.Field;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.InvocationException;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.StringReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Type;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.MethodBreakpoint;
import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
import org.netbeans.api.io.InputOutput;
import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
import org.netbeans.modules.debugger.jpda.jdi.ArrayReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ArrayTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassObjectReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.MethodWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ReferenceTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.StackFrameWrapper;
import org.netbeans.modules.debugger.jpda.jdi.StringReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.TypeComponentWrapper;
import org.netbeans.modules.debugger.jpda.jdi.TypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
import org.netbeans.modules.debugger.jpda.visual.spi.RemoteScreenshot;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.Pair;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakSet;
/**
*
* @author Martin Entlicher
*/
public class RemoteServices {
public static enum ServiceType {
AWT, FX
}
private static final Logger logger = Logger.getLogger(RemoteServices.class.getName());
private static final String REMOTE_CLASSES_ZIPFILE = "/org/netbeans/modules/debugger/jpda/visual/resources/debugger-remote.zip";
private static final Map<JPDADebugger, Map<ServiceType, ClassObjectReference>> remoteServiceClasses = new WeakHashMap<>();
private static final Map<JPDADebugger, Boolean> remoteServiceAccess = new WeakHashMap<JPDADebugger, Boolean>();
private static final RequestProcessor AUTORESUME_AFTER_SUSPEND_RP = new RequestProcessor("Autoresume after suspend", 1);
private static final Set<PropertyChangeListener> serviceListeners = new WeakSet<PropertyChangeListener>();
private RemoteServices() {}
public static void addServiceListener(PropertyChangeListener listener) {
synchronized (serviceListeners) {
serviceListeners.add(listener);
}
}
private static void fireServiceClass(JPDADebugger debugger) {
PropertyChangeEvent pche = new PropertyChangeEvent(RemoteServices.class, "serviceClass", null, debugger);
PropertyChangeListener[] listeners;
synchronized (serviceListeners) {
listeners = serviceListeners.toArray(new PropertyChangeListener[]{});
}
for (PropertyChangeListener l : listeners) {
l.propertyChange(pche);
}
}
private static ObjectReference getBootstrapClassLoader(ThreadReference tawt, VirtualMachine vm) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
/* Run this code:
ClassLoader cl = ClassLoader.getSystemClassLoader();
ClassLoader bcl;
do {
bcl = cl;
cl = cl.getParent();
} while (cl != null);
return bcl;
*/
ClassType classLoaderClass = getClass(vm, ClassLoader.class.getName());
Method getSystemClassLoader = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
ObjectReference cl = (ObjectReference) ClassTypeWrapper.invokeMethod(classLoaderClass, tawt, getSystemClassLoader, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
Method getParent = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getParent", "()Ljava/lang/ClassLoader;");
ObjectReference bcl;
do {
bcl = cl;
if ("sun.misc.Launcher$AppClassLoader".equals(cl.referenceType().name())) { // NOI18N
break;
}
cl = (ObjectReference) ObjectReferenceWrapper.invokeMethod(cl, tawt, getParent, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
} while (cl != null);
return bcl;
}
private static ObjectReference getContextClassLoader(ThreadReference tawt, VirtualMachine vm) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
ReferenceType threadType = tawt.referenceType();
Method getContextCl = ClassTypeWrapper.concreteMethodByName((ClassType) threadType, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
ObjectReference cl = (ObjectReference) ObjectReferenceWrapper.invokeMethod(tawt, tawt, getContextCl, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
ClassType classLoaderClass = null;
if (cl == null) {
classLoaderClass = getClass(vm, ClassLoader.class.getName());
Method getSystemClassLoader = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
cl = (ObjectReference) ClassTypeWrapper.invokeMethod(classLoaderClass, tawt, getSystemClassLoader, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
}
return cl;
}
private static ObjectReference getQuantumTookitClassLoader(VirtualMachine vm) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
ClassType classQuantumToolkit = getClass(vm, "com.sun.javafx.tk.quantum.QuantumToolkit");
if (classQuantumToolkit == null) {
return null;
}
ClassLoaderReference cl = ReferenceTypeWrapper.classLoader(classQuantumToolkit);
return cl;
}
public static ClassObjectReference uploadBasicClasses(JPDAThreadImpl t, ServiceType sType) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
ThreadReference tawt = t.getThreadReference();
VirtualMachine vm = tawt.virtualMachine();
t.notifyMethodInvoking();
try {
ClassObjectReference basicClass = null;
t.accessLock.writeLock().lock();
try {
List<RemoteClass> remoteClasses = getRemoteClasses();
for (RemoteClass rc : remoteClasses) {
String className = rc.name;
if (basicClass == null && className.indexOf('$') < 0 && className.endsWith("Service")) {
if ((sType == ServiceType.AWT && className.contains("AWT")) ||
(sType == ServiceType.FX && className.contains("FX"))) {
List<ReferenceType> classesByName = VirtualMachineWrapper.classesByName(vm, className);
if (!classesByName.isEmpty()) {
basicClass = ReferenceTypeWrapper.classObject(classesByName.get(0));
}
}
break;
}
}
// Suppose that when there's the basic class loaded, there are all.
if (basicClass == null) { // Load the classes only if there's not the basic one.
ObjectReference cl;
if (sType == ServiceType.AWT) {
cl = getBootstrapClassLoader(tawt, vm);
} else {
cl = getQuantumTookitClassLoader(vm);
if (cl == null) {
cl = getContextClassLoader(tawt, vm);
}
}
ClassType classLoaderClass = (ClassType) ObjectReferenceWrapper.referenceType(cl);
ByteValue[] mirrorBytesCache = new ByteValue[256];
for (RemoteClass rc : remoteClasses) {
String className = rc.name;
if ((sType == ServiceType.AWT && className.contains("AWT")) ||
(sType == ServiceType.FX && className.contains("FX"))) {
ClassObjectReference theUploadedClass;
ArrayReference byteArray = createTargetBytes(vm, rc.bytes, mirrorBytesCache);
StringReference nameMirror = null;
try {
Method defineClass = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
boolean uploaded = false;
while (!uploaded) {
nameMirror = VirtualMachineWrapper.mirrorOf(vm, className);
try {
ObjectReferenceWrapper.disableCollection(nameMirror);
uploaded = true;
} catch (ObjectCollectedExceptionWrapper ocex) {
// Just collected, try again...
}
}
uploaded = false;
while (!uploaded) {
theUploadedClass = (ClassObjectReference) ObjectReferenceWrapper.invokeMethod(cl, tawt, defineClass, Arrays.asList(nameMirror, byteArray, vm.mirrorOf(0), vm.mirrorOf(rc.bytes.length)), ObjectReference.INVOKE_SINGLE_THREADED);
if (basicClass == null && rc.name.indexOf('$') < 0 && rc.name.endsWith("Service")) {
try {
// Disable collection only of the basic class
ObjectReferenceWrapper.disableCollection(theUploadedClass);
basicClass = theUploadedClass;
uploaded = true;
} catch (ObjectCollectedExceptionWrapper ocex) {
// Just collected, try again...
}
} else {
uploaded = true;
}
}
} finally {
ObjectReferenceWrapper.enableCollection(byteArray); // We can dispose it now
if (nameMirror != null) {
ObjectReferenceWrapper.enableCollection(nameMirror);
}
}
}
//Method resolveClass = classLoaderClass.concreteMethodByName("resolveClass", "(Ljava/lang/Class;)V");
//systemClassLoader.invokeMethod(tawt, resolveClass, Arrays.asList(theUploadedClass), ObjectReference.INVOKE_SINGLE_THREADED);
}
}
if (basicClass != null) {
// Initialize the class:
ClassType bc = ((ClassType) basicClass.reflectedType());
if (!bc.isInitialized()) {
// Trying to initialize the class
ClassType theClass = getClass(vm, Class.class.getName());
// Call some method that will prepare the class:
Method aMethod = ClassTypeWrapper.concreteMethodByName(theClass, "getConstructors", "()[Ljava/lang/reflect/Constructor;");
ObjectReferenceWrapper.invokeMethod(basicClass, tawt, aMethod, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
}
}
} finally {
t.accessLock.writeLock().unlock();
}
if (basicClass != null) {
synchronized (remoteServiceClasses) {
Map<ServiceType, ClassObjectReference> basicClassesByType = remoteServiceClasses.get(t.getDebugger());
if (basicClassesByType == null) {
basicClassesByType = new HashMap<>();
remoteServiceClasses.put(t.getDebugger(), basicClassesByType);
}
basicClassesByType.put(sType, basicClass);
t.getDebugger().addPropertyChangeListener(new RemoteServiceDebuggerListener());
}
fireServiceClass(t.getDebugger());
}
return basicClass;
} finally {
t.notifyMethodInvokeDone();
}
}
static Pair<ClassType, Field> setPreferredEQThread(JPDAThread t, ServiceType sType) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper {
ClassObjectReference serviceClassObject = RemoteServices.getServiceClass(((JPDAThreadImpl) t).getDebugger(), sType);
if (serviceClassObject == null) {
return null;
}
final ClassType serviceClass;
try {
serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);
} catch (ObjectCollectedExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
return null;
}
Field preferredEventThreadField = null;
try {
preferredEventThreadField = ReferenceTypeWrapper.fieldByName(serviceClass, "preferredEventThread");
} catch (ObjectCollectedExceptionWrapper | ClassNotPreparedExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
}
if (preferredEventThreadField != null) {
try {
ClassTypeWrapper.setValue(serviceClass, preferredEventThreadField, ((JPDAThreadImpl) t).getThreadReference());
} catch(ClassNotLoadedException | ClassNotPreparedExceptionWrapper | InvalidTypeException ex) {
Exceptions.printStackTrace(ex);
}
return Pair.of(serviceClass, preferredEventThreadField);
} else {
return null;
}
}
static void clearPreferredEQThread(JPDADebugger dbg, Pair<ClassType, Field> preferredEventThreadFieldAndServiceClass) {
if (preferredEventThreadFieldAndServiceClass != null) {
final ClassType serviceClass = preferredEventThreadFieldAndServiceClass.first();
final Field preferredEventThreadField = preferredEventThreadFieldAndServiceClass.second();
try {
ClassTypeWrapper.setValue(serviceClass, preferredEventThreadField, null);
} catch(ClassNotLoadedException | ClassNotPreparedExceptionWrapper | InvalidTypeException ex) {
Exceptions.printStackTrace(ex);
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {}
}
}
private static void runOnBreakpoint(final JPDAThread awtThread, String bpClass, String bpMethod, final Runnable runnable, final CountDownLatch latch) {
final MethodBreakpoint mb = MethodBreakpoint.create(bpClass, bpMethod);
final JPDADebugger dbg = ((JPDAThreadImpl)awtThread).getDebugger();
final PropertyChangeListener[] listenerPtr = new PropertyChangeListener[] { null };
mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
mb.setSuspend(MethodBreakpoint.SUSPEND_EVENT_THREAD);
mb.setHidden(true);
mb.setThreadFilters(dbg, new JPDAThread[] { awtThread });
mb.addJPDABreakpointListener(new JPDABreakpointListener() {
@Override
public void breakpointReached(JPDABreakpointEvent event) {
if (dbg.equals(event.getDebugger())) {
try {
DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
//System.err.println("BREAKPOINT "+mb+" REMOVED after reached."+" ID = "+System.identityHashCode(mb));
PropertyChangeListener listener = listenerPtr[0];
if (listener != null) {
dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, listener);
listenerPtr[0] = null;
}
try {
((JPDAThreadImpl)awtThread).notifyMethodInvoking();
runnable.run();
} catch (PropertyVetoException e) {
} finally {
((JPDAThreadImpl)awtThread).notifyMethodInvokeDone();
}
} finally {
event.resume();
latch.countDown();
}
}
}
});
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (dbg.getState() == JPDADebugger.STATE_DISCONNECTED) {
DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
//System.err.println("BREAKPOINT "+mb+" REMOVED after debugger finished."+" ID = "+System.identityHashCode(mb));
dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
listenerPtr[0] = null;
latch.countDown();
}
}
};
dbg.addPropertyChangeListener(JPDADebugger.PROP_STATE, listener);
listenerPtr[0] = listener;
if (dbg.getState() != JPDADebugger.STATE_DISCONNECTED) {
DebuggerManager.getDebuggerManager().addBreakpoint(mb);
//System.err.println("ADD BP: "+mb+" ID = "+System.identityHashCode(mb));
} else {
dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, listener);
//System.err.println("NOT ADDED BP: "+mb+" ID = "+System.identityHashCode(mb));
latch.countDown();
}
}
private static final Map<JPDAThread, RequestProcessor.Task> tasksByThreads = new WeakHashMap<JPDAThread, RequestProcessor.Task> ();
/**
* Run the provided runnable after the thread is assured to be stopped on an event.
* If the thread was initially running, it's resumed with some delay
* (to allow another execution of runOnStoppedThread() without the expensive thread preparation).
* It's assumed that the runnable will invoke methods on the thread.
* Therefore method invoke notification methods are executed automatically.
* @param thread The remote thread.
* @param run The Runnable that is executed when the thread is assured to be stopped on an event.
* @throws PropertyVetoException when can not invoke methods.
*/
public static void runOnStoppedThread(JPDAThread thread, final Runnable run, ServiceType sType) throws PropertyVetoException {
final JPDAThreadImpl t = (JPDAThreadImpl) thread;
Lock lock = t.accessLock.writeLock();
lock.lock();
Pair<ClassType, Field> setPreferredEQThreadField;
try {
setPreferredEQThreadField = setPreferredEQThread(thread, sType);
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
return ;
}
try {
ThreadReference threadReference = t.getThreadReference();
boolean wasSuspended = t.isSuspended();
if (t.isSuspended() && !threadReference.isAtBreakpoint()) {
// TODO: Suspended, but will not be able to invoke methods
}
if (!t.isSuspended()) {
final CountDownLatch latch = new CountDownLatch(1);
lock.unlock();
lock = null;
VirtualMachine vm = ((JPDAThreadImpl) thread).getThreadReference().virtualMachine();
ClassObjectReference serviceClassObject;
synchronized (remoteServiceClasses) {
Map<ServiceType, ClassObjectReference> sc = remoteServiceClasses.get(((JPDAThreadImpl) thread).getDebugger());
if (sc != null) {
serviceClassObject = sc.get(sType);
} else {
serviceClassObject = null;
}
}
if (serviceClassObject == null) {
// The debugger session has finished already, do not run anything.
return ;
}
switch(sType) {
case AWT: {
runOnBreakpoint(
thread,
"org.netbeans.modules.debugger.jpda.visual.remote.RemoteAWTService", // NOI18N
"calledInAWT", // NOI18N
run,
latch
);
try {
ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);//getClass(vm, "org.netbeans.modules.debugger.jpda.visual.remote.RemoteService");
Field awtAccess = ReferenceTypeWrapper.fieldByName(serviceClass, "awtAccess"); // NOI18N
ClassTypeWrapper.setValue(serviceClass, awtAccess, VirtualMachineWrapper.mirrorOf(vm, true));
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vmdex) {
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
break;
}
case FX: {
runOnBreakpoint(
thread,
"org.netbeans.modules.debugger.jpda.visual.remote.RemoteFXService", // NOI18N
"access", // NOI18N
run,
latch
);
try {
ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);//getClass(vm, "org.netbeans.modules.debugger.jpda.visual.remote.RemoteService");
Field fxAccess = ReferenceTypeWrapper.fieldByName(serviceClass, "fxAccess"); // NOI18N
ClassTypeWrapper.setValue(serviceClass, fxAccess, VirtualMachineWrapper.mirrorOf(vm, true));
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vmdex) {
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
break;
}
}
try {
// wait for the async operation to finish
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} else {
RequestProcessor.Task autoresumeTask;
if (!wasSuspended) {
AutoresumeTask resumeTask = new AutoresumeTask(t);
autoresumeTask = AUTORESUME_AFTER_SUSPEND_RP.create(resumeTask);
synchronized (tasksByThreads) {
tasksByThreads.put(thread, autoresumeTask);
}
} else {
synchronized (tasksByThreads) {
autoresumeTask = tasksByThreads.get(thread);
}
}
t.notifyMethodInvoking();
if (autoresumeTask != null) {
autoresumeTask.schedule(Integer.MAX_VALUE); // wait for run.run() to finish...
}
try {
run.run();
} finally {
t.notifyMethodInvokeDone();
if (autoresumeTask != null) {
autoresumeTask.schedule(AutoresumeTask.WAIT_TIME);
}
}
}
} finally {
clearPreferredEQThread(t.getDebugger(), setPreferredEQThreadField);
if (lock != null) {
lock.unlock();
}
}
}
public static List<RemoteListener> getAttachedListeners(final JavaComponentInfo ci,
final boolean combineAllTypes) throws PropertyVetoException {
final List<RemoteListener> rlisteners = new ArrayList<RemoteListener>();
final JPDAThreadImpl thread = ci.getThread();
final ObjectReference component = ci.getComponent();
Pair<ClassType, Field> setPreferredEQThreadField;
try {
setPreferredEQThreadField = setPreferredEQThread(thread, ServiceType.AWT);
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
return rlisteners;
}
try {
runOnStoppedThread(thread, new Runnable() {
@Override
public void run() {
if (ci instanceof RemoteAWTScreenshot.AWTComponentInfo) {
retrieveAttachedListeners(thread, component, rlisteners, combineAllTypes);
} else {
retrieveAttachedFXListeners(thread, component, rlisteners);
}
}
}, (ci instanceof RemoteAWTScreenshot.AWTComponentInfo) ? ServiceType.AWT : ServiceType.FX);
} finally {
clearPreferredEQThread(thread.getDebugger(), setPreferredEQThreadField);
}
return rlisteners;
}
private static void retrieveAttachedListeners(JPDAThreadImpl thread,
ObjectReference component,
List<RemoteListener> rlisteners,
final boolean combineAllTypes) {
ThreadReference t = thread.getThreadReference();
try {
ReferenceType clazz = ObjectReferenceWrapper.referenceType(component);
List<Method> visibleMethods = ReferenceTypeWrapper.visibleMethods(clazz);
Map<ObjectReference, RemoteListener> listenersByInstance = null;
if (combineAllTypes) {
listenersByInstance = new HashMap<ObjectReference, RemoteListener>();
}
for (Method m : visibleMethods) {
String name = TypeComponentWrapper.name(m);
if (!name.startsWith("get") || !name.endsWith("Listeners")) {
continue;
}
if (MethodWrapper.argumentTypeNames(m).size() > 0) {
continue;
}
Value result;
try {
result = ObjectReferenceWrapper.invokeMethod(component, t, m, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
} catch (ClassNotLoadedException cnlex) {
continue;
} catch (InvocationException iex) {
Exceptions.printStackTrace(iex);
continue;
}
String listenerType = null;
try {
Type returnType = MethodWrapper.returnType(m);
if (returnType instanceof ArrayType) {
ArrayType art = (ArrayType) returnType;
listenerType = ArrayTypeWrapper.componentTypeName(art);
}
} catch (ClassNotLoadedException ex) {
continue;
}
if (listenerType == null) {
continue;
}
ArrayReference array = (ArrayReference) result;
List<Value> listeners = ArrayReferenceWrapper.getValues(array);
for (Value v : listeners) {
if (combineAllTypes) {
RemoteListener rl = listenersByInstance.get((ObjectReference) v);
if (rl != null) {
rl.addType(listenerType);
continue;
}
}
RemoteListener rl = new RemoteListener(listenerType, (ObjectReference) v);
if (combineAllTypes) {
listenersByInstance.put((ObjectReference) v, rl);
}
rlisteners.add(rl);
}
}
} catch (ClassNotPreparedExceptionWrapper cnpex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vdex) {
} catch (IncompatibleThreadStateException itsex) {
Exceptions.printStackTrace(itsex);
} catch (InvalidTypeException itex) {
Exceptions.printStackTrace(itex);
}
}
private static void retrieveAttachedFXListeners(JPDAThreadImpl thread, ObjectReference component, List<RemoteListener> rlisteners) {
ThreadReference t = thread.getThreadReference();
try {
ReferenceType clazz = ObjectReferenceWrapper.referenceType(component);
List<Method> visibleMethods = ReferenceTypeWrapper.visibleMethods(clazz);
for (Method m : visibleMethods) {
String name = TypeComponentWrapper.name(m);
if (!name.startsWith("getOn")) {
continue;
}
if (MethodWrapper.argumentTypeNames(m).size() > 0) {
continue;
}
Value result;
try {
result = ObjectReferenceWrapper.invokeMethod(component, t, m, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
if (result == null) {
continue;
}
} catch (ClassNotLoadedException cnlex) {
continue;
} catch (InvocationException iex) {
Exceptions.printStackTrace(iex);
continue;
}
String listenerType = null;
try {
Type returnType = MethodWrapper.returnType(m);
if (TypeWrapper.name(returnType).equals("javafx.event.EventHandler")) {
listenerType = name.substring(5);
}
// if (returnType instanceof ArrayType) {
// ArrayType art = (ArrayType) returnType;
// listenerType = art.componentTypeName();
// }
} catch (ClassNotLoadedException ex) {
continue;
}
if (listenerType == null) {
continue;
}
RemoteListener rl = new RemoteListener(listenerType, (ObjectReference)result);
rlisteners.add(rl);
// ArrayReference array = (ArrayReference) result;
// List<Value> listeners = array.getValues();
// for (Value v : listeners) {
// RemoteListener rl = new RemoteListener(listenerType, (ObjectReference) v);
// rlisteners.add(rl);
// }
}
} catch (ClassNotPreparedExceptionWrapper cnpex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vdex) {
} catch (IncompatibleThreadStateException itsex) {
Exceptions.printStackTrace(itsex);
} catch (InvalidTypeException itex) {
Exceptions.printStackTrace(itex);
}
}
public static List<ReferenceType> getAttachableListeners(JavaComponentInfo ci) {
ObjectReference component = ci.getComponent();
List<ReferenceType> listenerClasses = new ArrayList<ReferenceType>();
try {
ReferenceType clazz = ObjectReferenceWrapper.referenceType(component);
List<Method> visibleMethods = ReferenceTypeWrapper.visibleMethods(clazz);
for (Method m : visibleMethods) {
String name = TypeComponentWrapper.name(m);
if (!name.startsWith("add") || !name.endsWith("Listener")) {
continue;
}
List<Type> argTypes;
try {
argTypes = MethodWrapper.argumentTypes(m);
} catch (ClassNotLoadedException ex) {
continue;
}
if (argTypes.size() != 1) {
continue;
}
Type t = argTypes.get(0);
if (!(t instanceof ReferenceType)) {
continue;
}
ReferenceType rt = (ReferenceType) t;
String lname = ReferenceTypeWrapper.name(rt);
int i = lname.lastIndexOf('.');
if (i < 0) {
i = 0;
} else {
i++;
}
int ii = lname.lastIndexOf('$', i);
if (ii > i) {
i = ii + 1;
}
//System.err.println(" getAttachableListeners() '"+name.substring(3)+"' should equal to '"+lname.substring(i)+"', lname = "+lname+", i = "+i);
if (!name.substring(3).equals(lname.substring(i))) {
// addXXXListener() method name does not match XXXListener simple class name.
// TODO: Perhaps check removeXXXListener method instead of this.
continue;
}
listenerClasses.add(rt);
}
} catch (ClassNotPreparedExceptionWrapper cnpex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vdex) {
}
return listenerClasses;
}
/*
private static final Map<JavaComponentInfo, Map<ClassObjectReference, Set<LoggingListenerCallBack>>> loggingListeners =
new WeakHashMap<JavaComponentInfo, Map<ClassObjectReference, Set<LoggingListenerCallBack>>>();
*/
private static final Map<JPDADebugger, LoggingListeners> loggingListeners =
new WeakHashMap<JPDADebugger, LoggingListeners>();
private static final class LoggingListeners {
private final Map<ObjectReference, Map<ClassObjectReference, Set<LoggingListenerCallBack>>> componentListeners =
new HashMap<ObjectReference, Map<ClassObjectReference, Set<LoggingListenerCallBack>>>();
static LoggingListeners get(JPDADebugger dbg) {
synchronized (loggingListeners) {
return loggingListeners.get(dbg);
}
}
private synchronized boolean add(ObjectReference component, ClassObjectReference listenerClass, LoggingListenerCallBack listener) {
Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
if (listeners == null) {
listeners = new HashMap<ClassObjectReference, Set<LoggingListenerCallBack>>();
componentListeners.put(component, listeners);
}
Set<LoggingListenerCallBack> lcb = listeners.get(listenerClass);
if (lcb == null) {
lcb = new HashSet<LoggingListenerCallBack>();
listeners.put(listenerClass, lcb);
}
return lcb.add(listener);
}
private synchronized boolean remove(ObjectReference component, ClassObjectReference listenerClass, LoggingListenerCallBack listener) {
Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
if (listeners == null) {
return false;
}
Set<LoggingListenerCallBack> lcb = listeners.get(listenerClass);
if (lcb == null) {
return false;
}
boolean removed = lcb.remove(listener);
if (removed) {
if (lcb.isEmpty()) {
listeners.remove(listenerClass);
if (listeners.isEmpty()) {
componentListeners.remove(component);
}
}
}
return removed;
}
synchronized Set<LoggingListenerCallBack> getListeners(ObjectReference component, ClassObjectReference listenerClass) {
Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
if (listeners == null) {
return null;
}
return new HashSet<>(listeners.get(listenerClass));
}
private synchronized boolean isEmpty() {
return componentListeners.isEmpty();
}
}
private static void addEventsLoggingBreakpoint(final JPDADebugger dbg) {
final MethodBreakpoint mb = MethodBreakpoint.create("org.netbeans.modules.debugger.jpda.visual.remote.RemoteAWTService", "calledWithEventsData");
mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
mb.setSuspend(MethodBreakpoint.SUSPEND_EVENT_THREAD);
mb.setHidden(true);
//final Object bpLock = new Object();
mb.addJPDABreakpointListener(new JPDABreakpointListener() {
@Override
public void breakpointReached(JPDABreakpointEvent event) {
//synchronized (bpLock) {
//DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
try {
ThreadReference tr = ((JPDAThreadImpl) event.getThread()).getThreadReference();
StackFrame topFrame;
List<Value> argumentValues;
try {
topFrame = ThreadReferenceWrapper.frame(tr, 0);
argumentValues = StackFrameWrapper.getArgumentValues(topFrame);
} catch (InternalExceptionWrapper ex) {
return ;
} catch (VMDisconnectedExceptionWrapper ex) {
return ;
} catch (ObjectCollectedExceptionWrapper ex) {
return ;
} catch (IllegalThreadStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
return ;
} catch (IncompatibleThreadStateException ex) {
Exceptions.printStackTrace(ex);
return;
} catch (InvalidStackFrameExceptionWrapper isfex) {
Exceptions.printStackTrace(isfex);
return;
}
//System.err.println("LoggingListener breakpoint reached: argumentValues = "+argumentValues);
if (argumentValues.size() < 3) { // ERROR: BP is hit somewhere else
logger.info("Warning: attachLoggingListener().breakpointReached(): argumentValues.size() = "+argumentValues.size());
return;
}
LoggingListeners ll = LoggingListeners.get(dbg);
if (ll == null) {
return ;
}
Value component = argumentValues.get(0);
Value listenerClass = argumentValues.get(1);
Set<LoggingListenerCallBack> listeners = ll.getListeners((ObjectReference) component, (ClassObjectReference) listenerClass);
if (listeners == null) {
return;
}
ArrayReference allDataArray = (ArrayReference) argumentValues.get(2);
try {
int totalLength = ArrayReferenceWrapper.length(allDataArray);
List<Value> dataValues = ArrayReferenceWrapper.getValues(allDataArray);
String[] eventProps = null;
for (int i = 0; i < totalLength; ) {
StringReference sr = (StringReference) dataValues.get(i);
String dataLengthStr = StringReferenceWrapper.value(sr);
//System.err.println(" data["+i+"] = "+dataLengthStr);
int dataLength;
try {
dataLength = Integer.parseInt(dataLengthStr);
} catch (NumberFormatException nfex) {
Exceptions.printStackTrace(Exceptions.attachMessage(nfex, "Data length string = '"+dataLengthStr+"'"));
return;
}
String[] data = new String[dataLength];
i++;
for (int j = 0; j < dataLength; j++, i++) {
sr = (StringReference) dataValues.get(i);
data[j] = StringReferenceWrapper.value(sr);
//System.err.println(" data["+i+"] = "+data[j]);
}
if (eventProps == null) {
eventProps = data;
} else {
//System.err.println("eventsData("+ci+", "+eventProps+", "+data+") passed to "+listener);
for (LoggingListenerCallBack listener : listeners) {
listener.eventsData(/*ci,*/ eventProps, data/*stack*/);
}
eventProps = null;
}
}
} catch (InternalExceptionWrapper iex) {
} catch (NumberFormatException nfex) {
Exceptions.printStackTrace(nfex);
} catch (ObjectCollectedExceptionWrapper ocex) {
Exceptions.printStackTrace(ocex);
} catch (VMDisconnectedExceptionWrapper vmdex) {
}
} finally {
event.resume();
}
}
});
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (dbg.getState() == JPDADebugger.STATE_DISCONNECTED) {
DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
//System.err.println("BREAKPOINT "+mb+" REMOVED after debugger finished."+" ID = "+System.identityHashCode(mb));
dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
}
}
};
dbg.addPropertyChangeListener(JPDADebugger.PROP_STATE, listener);
if (dbg.getState() != JPDADebugger.STATE_DISCONNECTED) {
DebuggerManager.getDebuggerManager().addBreakpoint(mb);
} else {
dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, listener);
}
}
public static ObjectReference attachLoggingListener(final JavaComponentInfo ci,
final ClassObjectReference listenerClass,
final LoggingListenerCallBack listener) throws PropertyVetoException {
final JPDAThreadImpl thread = ci.getThread();
final ObjectReference[] listenerPtr = new ObjectReference[] { null };
runOnStoppedThread(thread, new Runnable() {
@Override
public void run() {
ThreadReference t = thread.getThreadReference();
ObjectReference component = ci.getComponent();
JPDADebugger dbg = ci.getThread().getDebugger();
LoggingListeners ll;
boolean newLL;
synchronized (loggingListeners) {
ll = loggingListeners.get(dbg);
newLL = ll == null;
if (newLL) {
ll = new LoggingListeners();
loggingListeners.put(dbg, ll);
}
ll.add(ci.getComponent(), listenerClass, listener);
}
if (newLL) {
addEventsLoggingBreakpoint(dbg);
}
ClassObjectReference serviceClassObject;
synchronized (remoteServiceClasses) {
serviceClassObject = remoteServiceClasses.get(thread.getDebugger()).get(ServiceType.AWT);
}
try {
ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);//getClass(vm, "org.netbeans.modules.debugger.jpda.visual.remote.RemoteService");
Method addLoggingListener = ClassTypeWrapper.concreteMethodByName(serviceClass, "addLoggingListener", "(Ljava/awt/Component;Ljava/lang/Class;)Ljava/lang/Object;");
ObjectReference theListener = (ObjectReference)
ClassTypeWrapper.invokeMethod(serviceClass, t, addLoggingListener, Arrays.asList(component, listenerClass), ObjectReference.INVOKE_SINGLE_THREADED);
listenerPtr[0] = theListener;
} catch (InvalidTypeException ex) {
Exceptions.printStackTrace(ex);
} catch (ClassNotLoadedException ex) {
Exceptions.printStackTrace(ex);
} catch (IncompatibleThreadStateException ex) {
Exceptions.printStackTrace(ex);
} catch (InvocationException ex) {
Exceptions.printStackTrace(ex);
} catch (ClassNotPreparedExceptionWrapper cnpex) {
Exceptions.printStackTrace(cnpex);
} catch (InternalExceptionWrapper iex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
Exceptions.printStackTrace(ocex);
} catch (VMDisconnectedExceptionWrapper vmdex) {
}
}
}, ServiceType.AWT);
return listenerPtr[0];
}
public static boolean detachLoggingListener(final JavaComponentInfo ci,
final ClassObjectReference listenerClass,
final ObjectReference listener) throws PropertyVetoException {
final JPDAThreadImpl thread = ci.getThread();
final boolean[] retPtr = new boolean[] { false };
runOnStoppedThread(thread, new Runnable() {
@Override
public void run() {
ObjectReference component = ci.getComponent();
ThreadReference t = thread.getThreadReference();
ClassObjectReference serviceClassObject;
synchronized (remoteServiceClasses) {
serviceClassObject = remoteServiceClasses.get(thread.getDebugger()).get(ServiceType.AWT);
}
try {
ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);
Method removeLoggingListener = ClassTypeWrapper.concreteMethodByName(serviceClass, "removeLoggingListener", "(Ljava/awt/Component;Ljava/lang/Class;Ljava/lang/Object;)Z");
BooleanValue success = (BooleanValue)
ClassTypeWrapper.invokeMethod(serviceClass, t, removeLoggingListener, Arrays.asList(component, listenerClass, listener), ObjectReference.INVOKE_SINGLE_THREADED);
retPtr[0] = success.value();
} catch (InvalidTypeException ex) {
Exceptions.printStackTrace(ex);
} catch (ClassNotLoadedException ex) {
Exceptions.printStackTrace(ex);
} catch (IncompatibleThreadStateException ex) {
Exceptions.printStackTrace(ex);
} catch (InvocationException ex) {
Exceptions.printStackTrace(ex);
} catch (ClassNotPreparedExceptionWrapper cnpex) {
Exceptions.printStackTrace(cnpex);
} catch (InternalExceptionWrapper iex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
Exceptions.printStackTrace(ocex);
} catch (VMDisconnectedExceptionWrapper vmdex) {
}
JPDADebugger dbg = ci.getThread().getDebugger();
synchronized (loggingListeners) {
LoggingListeners ll = loggingListeners.get(dbg);
if (ll != null) { // should be so
Set<LoggingListenerCallBack> listeners = ll.getListeners(component, listenerClass);
for (LoggingListenerCallBack llcb : listeners) {
if (listener.equals(llcb.getListenerObject())) {
ll.remove(component, listenerClass, llcb);
}
}
}
}
}
}, ServiceType.AWT);
return retPtr[0];
}
static void attachHierarchyListeners(final boolean attach, ServiceType sType) {
final Set<Entry<JPDADebugger, ClassObjectReference>> serviceClasses;
synchronized (remoteServiceClasses) {
serviceClasses = new HashSet<>();
remoteServiceClasses.entrySet().forEach(entry -> {
ClassObjectReference cor = entry.getValue().get(sType);
if (cor != null) {
serviceClasses.add(new AbstractMap.SimpleEntry<>(entry.getKey(), cor));
}
});
}
for (Entry<JPDADebugger, ClassObjectReference> serviceEntry : serviceClasses) {
JPDADebugger debugger = serviceEntry.getKey();
ClassObjectReference cor = serviceEntry.getValue();
final ClassType serviceClass;
try {
serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(cor);
} catch (InternalExceptionWrapper ex) {
continue;
} catch (VMDisconnectedExceptionWrapper ex) {
continue;
} catch (ObjectCollectedExceptionWrapper ex) {
continue;
}
List<JPDAThread> allThreads = debugger.getThreadsCollector().getAllThreads();
JPDAThread thread = null;
for (JPDAThread t : allThreads) {
if (sType == ServiceType.AWT && t.getName().startsWith(RemoteAWTScreenshot.AWTThreadName)) {
thread = t;
}
}
if (thread != null) {
final JPDAThread t = thread;
final ThreadReference tr = ((JPDAThreadImpl) t).getThreadReference();
try {
runOnStoppedThread(t, new Runnable() {
@Override
public void run() {
try {
if (attach) {
Method startHierarchyListenerMethod = ClassTypeWrapper.concreteMethodByName(serviceClass, "startHierarchyListener", "()Ljava/lang/String;");
Value res = ClassTypeWrapper.invokeMethod(serviceClass, tr, startHierarchyListenerMethod, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
if (res instanceof StringReference) {
String reason = ((StringReference) res).value();
InputOutput io = ((JPDAThreadImpl) t).getDebugger().getConsoleIO().getIO();
if (io != null) {
io.getErr().println(NbBundle.getMessage(VisualDebuggerListener.class, "MSG_NoTrackingOfComponentChanges", reason));
}
}
} else {
Method stopHierarchyListenerMethod = ClassTypeWrapper.concreteMethodByName(serviceClass, "stopHierarchyListener", "()V");
ClassTypeWrapper.invokeMethod(serviceClass, tr, stopHierarchyListenerMethod, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
}
} catch (VMDisconnectedExceptionWrapper vmd) {
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
}, sType);
} catch (PropertyVetoException ex) {
}
}
}
}
static void setAccessLoopStarted(JPDADebugger debugger, boolean success) {
synchronized (remoteServiceAccess) {
remoteServiceAccess.put(debugger, success);
}
fireServiceClass(debugger);
}
public static boolean hasServiceAccess(JPDADebugger debugger) {
Map<ServiceType, ClassObjectReference> cs;
synchronized (remoteServiceClasses) {
cs = remoteServiceClasses.get(debugger);
}
if (cs != null) {
Boolean has;
synchronized (remoteServiceAccess) {
has = remoteServiceAccess.get(debugger);
}
return has != null && has.booleanValue();
} else {
return false;
}
}
public static ClassObjectReference getServiceClass(JPDADebugger debugger, ServiceType sType) {
synchronized (remoteServiceClasses) {
Map<ServiceType, ClassObjectReference> cs = remoteServiceClasses.get(debugger);
if (cs == null) {
return null;
} else {
return cs.get(sType);
}
}
}
static ClassType getClass(VirtualMachine vm, String name) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
List<ReferenceType> classList = VirtualMachineWrapper.classesByName(vm, name);
ReferenceType clazz = null;
for (ReferenceType c : classList) {
if (ReferenceTypeWrapper.classLoader(c) == null) {
clazz = c;
break;
}
}
if (clazz == null && classList.size() > 0) {
clazz = classList.get(0);
}
return (ClassType) clazz;
}
static ArrayType getArrayClass(VirtualMachine vm, String name) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
List<ReferenceType> classList = VirtualMachineWrapper.classesByName(vm, name);
ReferenceType clazz = null;
for (ReferenceType c : classList) {
if (ReferenceTypeWrapper.classLoader(c) == null) {
clazz = c;
break;
}
}
return (ArrayType) clazz;
}
private static List<RemoteClass> getRemoteClasses() throws IOException {
InputStream in = RemoteServices.class.getResourceAsStream(REMOTE_CLASSES_ZIPFILE);
try {
ZipInputStream zin = new ZipInputStream(in);
ZipEntry ze;
List<RemoteClass> rcl = new ArrayList<RemoteClass>();
while((ze = zin.getNextEntry()) != null) {
String fileName = ze.getName();
if (!fileName.endsWith(".class")) {
continue;
}
String name = fileName.substring(0, fileName.length() - ".class".length());
int baseStart = name.lastIndexOf('/');
if (baseStart < 0) {
continue;
}
/*baseStart++;
int baseEnd = name.indexOf('$', baseStart);
if (baseEnd < 0) {
baseEnd = name.length();
}*/
RemoteClass rc = new RemoteClass();
rc.name = name.replace('/', '.');
int l = (int) ze.getSize();
byte[] bytes = new byte[l];
int num = 0;
while (num < l) {
int r = zin.read(bytes, num, l - num);
if (r < 0) {
Exceptions.printStackTrace(new IllegalStateException("Can not read full content of "+name+" entry. Length = "+l+", read num = "+num));
break;
}
num += r;
}
rc.bytes = bytes;
rcl.add(rc);
}
return rcl;
} finally {
in.close();
}
}
private static ArrayReference createTargetBytes(VirtualMachine vm, byte[] bytes,
ByteValue[] mirrorBytesCache) throws InvalidTypeException,
ClassNotLoadedException,
InternalExceptionWrapper,
VMDisconnectedExceptionWrapper,
ObjectCollectedExceptionWrapper,
UnsupportedOperationExceptionWrapper {
ArrayType bytesArrayClass = getArrayClass(vm, "byte[]");
ArrayReference array = null;
boolean disabledCollection = false;
while (!disabledCollection) {
array = ArrayTypeWrapper.newInstance(bytesArrayClass, bytes.length);
try {
ObjectReferenceWrapper.disableCollection(array);
disabledCollection = true;
} catch (ObjectCollectedExceptionWrapper ocex) {
// Collected too soon, try again...
}
}
List<Value> values = new ArrayList<Value>(bytes.length);
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
ByteValue mb = mirrorBytesCache[128 + b];
if (mb == null) {
mb = VirtualMachineWrapper.mirrorOf(vm, b);
mirrorBytesCache[128 + b] = mb;
}
values.add(mb);
}
ArrayReferenceWrapper.setValues(array, values);
return array;
}
private static class RemoteServiceDebuggerListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName())) {
JPDADebugger d = (JPDADebugger) evt.getSource();
if (JPDADebugger.STATE_DISCONNECTED == d.getState()) {
d.removePropertyChangeListener(this);
synchronized (remoteServiceClasses) {
remoteServiceClasses.remove(d);
}
}
}
}
}
private static class RemoteClass {
private String name;
private byte[] bytes;
}
public static class RemoteListener {
private String type;
private List<String> allTypesList;
private String[] allTypes;
//private String classType;
private ObjectReference l;
public RemoteListener(String type, ObjectReference l) {
this.type = type;
this.l = l;
}
public String getType() {
return type;
}
public void setAllTypes(String[] allTypes) {
this.allTypes = allTypes;
}
private void addType(String listenerType) {
if (allTypesList == null) {
allTypesList = new ArrayList<String>();
allTypesList.add(type);
}
allTypesList.add(listenerType);
}
public String[] getTypes() {
if (allTypes == null) {
if (allTypesList != null) {
allTypes = allTypesList.toArray(new String[] {});
} else {
allTypes = new String[] { type };
}
}
return allTypes;
}
//public String getClassType() {
// return classType;
//}
public ObjectReference getListener() {
return l;
}
@Override
public String toString() {
return "RemoteListener("+type+")["+l+"]";
}
}
public static interface LoggingListenerCallBack {
public void eventsData(/*JavaComponentInfo ci,*/ String[] data, String[] stack);
public ObjectReference getListenerObject();
}
private static class AutoresumeTask implements Runnable, PropertyChangeListener {
private static final int WAIT_TIME = 500;
private volatile JPDAThreadImpl t;
public AutoresumeTask(JPDAThreadImpl t) {
this.t = t;
t.addPropertyChangeListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
JPDAThreadImpl thread = this.t;
if (thread == null) {
return ;
}
if (JPDAThread.PROP_SUSPENDED.equals(evt.getPropertyName()) &&
!"methodInvoke".equals(evt.getPropagationId())) { // NOI18N
thread.removePropertyChangeListener(this);
logger.fine("AutoresumeTask: autoresume canceled, thread changed suspended state: suspended = "+thread.isSuspended());
synchronized (tasksByThreads) {
tasksByThreads.remove(thread);
}
t = null;
}
}
@Override
public void run() {
JPDAThreadImpl thread = this.t;
this.t = null;
if (thread != null) {
thread.removePropertyChangeListener(this);
thread.resume();
}
}
}
}