diff options
author | Keith Seitz <keiths@redhat.com> | 2007-05-18 18:33:46 +0000 |
---|---|---|
committer | Keith Seitz <keiths@redhat.com> | 2007-05-18 18:33:46 +0000 |
commit | 95dec624ccc2505d8ee9cb31202e8d1b3984dd36 (patch) | |
tree | 4fd1aab161a233135bd95527b116e1d6c32d7a32 | |
parent | 7795cc15121cf30cc1f5717257f1866fcbd9c9ff (diff) |
merge w/gcj trunk for JDWP/JVMTI
git-svn-id: https://gcc.gnu.org/svn/gcc/branches/redhat/gcc-4_1-jdwp-merge-branch@124829 138bc75d-0d04-0410-961f-82ee72b054a4
77 files changed, 5888 insertions, 923 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 1f6a4d52397..4e5a5afa613 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,104 @@ +2007-05-18 Keith Seitz <keiths@redhat.com> + + Merged from trunk: + + 2007-05-17 Kyle Galloway <kgallowa@redhat.com> + * classpath/gnu/classpath/jdwp/processor/ReferenceTypeCommandSet.java + (executeMethods): Remove cast to ClassReferenceTypeId. + * classpath/lib/gnu/classpath/jdwp/processor/ + ReferenceTypeCommandSet.class: Rebuilt. + + 2007-05-17 Kyle Galloway <kgallowa@redhat.com> + * gnu/classpath/jdwp/natVMMethod.cc (getModifiers): Check for native + classes and mark methods as native appropriately. + + 2007-05-17 Kyle Galloway <kgallowa@redhat.com> + * gnu/classpath/jdwp/VMFrame.java (<init>): Add parameter for "this" + pointer. + * gnu/classpath/jdwp/VMFrame.h: Regenerated. + * classpath/lib/gnu/classpath/jdwp/VMFrame.class: Rebuilt. + * gnu/classpath/jdwp/natVMVirtualMachine.cc (getFrame): Use new + VMFrame constructor. + + 2007-05-16 Keith Seitz <keiths@redhat.com> + * include/java-interp.h (breakpoint_at): Declare. + * interpret.cc (breakpoint_at): New function. + * gnu/classpath/jdwp/VMVirtualMachine.java (_event_list): + New member. + * gnu/classpath/jdwp/natVMVirtualMachine.cc (initialize): + Initialize _event_list. + (handle_single_step): If there is a breakpoint at the + location at which we are stopping, do not send the notification. + Instead add the event to a list of events that occur at this + location. + (jdwpBreakpointCB): If the event list is not empty, send + whatever events are in it and the breakpoint event in a single + notification. + Mark parameter jni_env as MAYBE_UNUSED. + * classpath/lib/gnu/classpath/jdwp/VMVirtualMachine.class: + Regenerated. + * gnu/classpath/jdwp/VMVirtualMachine.h: Regenerated. + + 2007-05-15 Keith Seitz <keiths@redhat.com> + * interpret.cc (STOREA): Rewrite using temporary variable to + avoid double-macro expansion side-effects. + (STOREI): Likewise. + (STOREF): Likewise. + (STOREL)[SIZEOF_VOID_P == 8]: Likewise. + (STORED)[SIZEOF_VOID_P == 8]: Likewise. + (STOREL)[SIZEOF_VOID_P != 8]: Likewise. + (STORED)[SIZEOF_VOID_P != 8]: Likewise. + (POKEI): Likewise. + + 2007-05-07 Keith Seitz <keiths@redhat.com> + * classpath/lib/gnu/classpath/jdwp/Jdwp.class: Regenerate. + * classpath/lib/gnu/classpath/jdwp/Jdwp$1.class: Regenerate. + * classpath/lib/gnu/classpath/jdwp/event/Event.class: + Regenerate. + * classpath/lib/gnu/classpath/jdwp/transport/JdwpConnection.class: + Regenerate. + * gnu/classpath/jdwp/Jdwp.h: Regenerate. + * gnu/classpath/jdwp/event/Event.h: Regenerate. + * gnu/classpath/jdwp/transport/JdwpConnection.h: Regenerate. + + 2007-05-04 Kyle Galloway <kgallowa@redhat.com> + * gnu/classpath/jdwp/natVMVirtualMachine.cc (getClassMethod): Change + to use JVMTI. + + 2007-05-03 Keith Seitz <keiths@redhat.com> + * interpret.cc: Don't include ExceptionEvent.h. + * gnu/gcj/jvmti/natExceptionEvent.cc: Remove. + * Makefile.am (nat_source_files): Remove natExceptionEvent.cc. + * Makefile.in: Regenerated. + + 2007-05-03 Keith Seitz <keiths@redhat.com> + * include/jvmti-int.h (_Jv_ReportJVMTIExceptionThrow): + Declare. + * interpret.cc (_Jv_ReportJVMTIExceptionThrow): New function. + (find_catch_location): New function. + (REPORT_EXCEPTION): New macro. + (throw_internal_error): Use REPORT_EXCEPTION. + (throw_incompatible_class_change_error): Likewise. + (throw_null_pointer_exception): Likewise. + (throw_class_format_error): Likewise. + * interpret-run.cc (INTERP_REPORT_EXCEPTION)[DEBUG]: Set + to REPORT_EXCEPTION. + (INTERP_REPORT_EXCEPTION)[!DEBUG]: Make nop. + (insn_new): Use INTERP_REPORT_EXCEPTION. + (insn_athrow): Likewise. + Remove previous JVMTI exception notifications. + Add JVMTI ExceptionCatch notificatin. + * jni.cc (_Jv_PopSystemFrame): Notify JVMTI clients of + exception throw. + * gnu/gcj/jvmti/ExceptionEvent.java: Removed. + * gnu/gcj/jvmti/ExceptionEvent.h: Removed. + * classpath/lib/gnu/gcj/jvmti/ExceptionEvent.class: Removed. + * gnu/classpath/jdwp/natVMVirtualMachine.cc + (jdwpExceptionCB): New function. + (jdwpVMInitCB): Set Exception event handler and enable. + * sources.am: Regenerated. + * Makefile.in: Regenerated. + 2007-05-03 Thomas Fitzsimmons <fitzsim@redhat.com> https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=237304 diff --git a/libjava/Makefile.in b/libjava/Makefile.in index 60fd06f35bc..0d1ef376b81 100644 --- a/libjava/Makefile.in +++ b/libjava/Makefile.in @@ -156,9 +156,10 @@ am__DEPENDENCIES_1 = gnu/awt.lo gnu/awt/j2d.lo gnu/classpath.lo \ gnu/classpath/jdwp/exception.lo gnu/classpath/jdwp/id.lo \ gnu/classpath/jdwp/processor.lo \ gnu/classpath/jdwp/transport.lo gnu/classpath/jdwp/util.lo \ - gnu/gcj.lo gnu/gcj/convert.lo gnu/gcj/io.lo gnu/gcj/jvmti.lo \ - gnu/gcj/runtime.lo gnu/gcj/util.lo gnu/java/awt.lo \ - gnu/java/awt/color.lo gnu/java/awt/dnd.lo gnu/java/awt/font.lo \ + gnu/classpath/jdwp/value.lo gnu/gcj.lo gnu/gcj/convert.lo \ + gnu/gcj/io.lo gnu/gcj/jvmti.lo gnu/gcj/runtime.lo \ + gnu/gcj/util.lo gnu/java/awt.lo gnu/java/awt/color.lo \ + gnu/java/awt/dnd.lo gnu/java/awt/font.lo \ gnu/java/awt/font/autofit.lo gnu/java/awt/font/opentype.lo \ gnu/java/awt/font/opentype/truetype.lo gnu/java/awt/image.lo \ gnu/java/awt/java2d.lo gnu/java/awt/peer.lo \ @@ -1188,15 +1189,19 @@ classpath/gnu/classpath/jdwp/event/filters/ThreadOnlyFilter.java gnu_classpath_jdwp_event_filters_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_event_filters_source_files))) gnu_classpath_jdwp_exception_source_files = \ +classpath/gnu/classpath/jdwp/exception/AbsentInformationException.java \ classpath/gnu/classpath/jdwp/exception/InvalidClassException.java \ classpath/gnu/classpath/jdwp/exception/InvalidClassLoaderException.java \ classpath/gnu/classpath/jdwp/exception/InvalidCountException.java \ classpath/gnu/classpath/jdwp/exception/InvalidEventTypeException.java \ classpath/gnu/classpath/jdwp/exception/InvalidFieldException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidFrameException.java \ classpath/gnu/classpath/jdwp/exception/InvalidLocationException.java \ classpath/gnu/classpath/jdwp/exception/InvalidMethodException.java \ classpath/gnu/classpath/jdwp/exception/InvalidObjectException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidSlotException.java \ classpath/gnu/classpath/jdwp/exception/InvalidStringException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidTagException.java \ classpath/gnu/classpath/jdwp/exception/InvalidThreadException.java \ classpath/gnu/classpath/jdwp/exception/InvalidThreadGroupException.java \ classpath/gnu/classpath/jdwp/exception/JdwpException.java \ @@ -1204,6 +1209,7 @@ classpath/gnu/classpath/jdwp/exception/JdwpIllegalArgumentException.java \ classpath/gnu/classpath/jdwp/exception/JdwpInternalErrorException.java \ classpath/gnu/classpath/jdwp/exception/NativeMethodException.java \ classpath/gnu/classpath/jdwp/exception/NotImplementedException.java \ +classpath/gnu/classpath/jdwp/exception/TypeMismatchException.java \ classpath/gnu/classpath/jdwp/exception/VmDeadException.java gnu_classpath_jdwp_exception_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_exception_source_files))) @@ -1215,6 +1221,7 @@ classpath/gnu/classpath/jdwp/id/ClassObjectId.java \ classpath/gnu/classpath/jdwp/id/ClassReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/InterfaceReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/JdwpId.java \ +classpath/gnu/classpath/jdwp/id/NullObjectId.java \ classpath/gnu/classpath/jdwp/id/ObjectId.java \ classpath/gnu/classpath/jdwp/id/ReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/StringId.java \ @@ -1259,11 +1266,30 @@ classpath/gnu/classpath/jdwp/util/JdwpString.java \ classpath/gnu/classpath/jdwp/util/LineTable.java \ classpath/gnu/classpath/jdwp/util/Location.java \ classpath/gnu/classpath/jdwp/util/MethodResult.java \ +classpath/gnu/classpath/jdwp/util/MonitorInfo.java \ +classpath/gnu/classpath/jdwp/util/NullObject.java \ classpath/gnu/classpath/jdwp/util/Signature.java \ classpath/gnu/classpath/jdwp/util/Value.java \ classpath/gnu/classpath/jdwp/util/VariableTable.java gnu_classpath_jdwp_util_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_util_source_files))) +gnu_classpath_jdwp_value_source_files = \ +classpath/gnu/classpath/jdwp/value/ArrayValue.java \ +classpath/gnu/classpath/jdwp/value/BooleanValue.java \ +classpath/gnu/classpath/jdwp/value/ByteValue.java \ +classpath/gnu/classpath/jdwp/value/CharValue.java \ +classpath/gnu/classpath/jdwp/value/DoubleValue.java \ +classpath/gnu/classpath/jdwp/value/FloatValue.java \ +classpath/gnu/classpath/jdwp/value/IntValue.java \ +classpath/gnu/classpath/jdwp/value/LongValue.java \ +classpath/gnu/classpath/jdwp/value/ObjectValue.java \ +classpath/gnu/classpath/jdwp/value/ShortValue.java \ +classpath/gnu/classpath/jdwp/value/StringValue.java \ +classpath/gnu/classpath/jdwp/value/Value.java \ +classpath/gnu/classpath/jdwp/value/ValueFactory.java \ +classpath/gnu/classpath/jdwp/value/VoidValue.java + +gnu_classpath_jdwp_value_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_value_source_files))) gnu_gcj_source_files = \ gnu/gcj/Core.java \ gnu/gcj/RawData.java \ @@ -7184,6 +7210,7 @@ all_packages_source_files = \ gnu/classpath/jdwp/processor.list \ gnu/classpath/jdwp/transport.list \ gnu/classpath/jdwp/util.list \ + gnu/classpath/jdwp/value.list \ gnu/gcj.list \ gnu/gcj/convert.list \ gnu/gcj/io.list \ @@ -7428,6 +7455,7 @@ ordinary_header_files = \ $(gnu_classpath_jdwp_processor_header_files) \ $(gnu_classpath_jdwp_transport_header_files) \ $(gnu_classpath_jdwp_util_header_files) \ + $(gnu_classpath_jdwp_value_header_files) \ $(gnu_gcj_header_files) \ $(gnu_gcj_convert_header_files) \ $(gnu_gcj_io_header_files) \ @@ -10004,6 +10032,12 @@ gnu/classpath/jdwp/util.list: $(gnu_classpath_jdwp_util_source_files) -include gnu/classpath/jdwp/util.deps +gnu/classpath/jdwp/value.list: $(gnu_classpath_jdwp_value_source_files) + @$(mkinstalldirs) $(dir $@) + echo $(srcdir)/classpath/lib/gnu/classpath/jdwp/value/*.class > gnu/classpath/jdwp/value.list + +-include gnu/classpath/jdwp/value.deps + gnu/gcj.list: $(gnu_gcj_source_files) @$(mkinstalldirs) $(dir $@) echo $(srcdir)/classpath/lib/gnu/gcj/*.class > gnu/gcj.list diff --git a/libjava/boehm.cc b/libjava/boehm.cc index 66860dd50da..3aa0acba987 100644 --- a/libjava/boehm.cc +++ b/libjava/boehm.cc @@ -722,6 +722,17 @@ _Jv_ResumeThread (_Jv_Thread_t *thread) #endif } +int +_Jv_IsThreadSuspended (_Jv_Thread_t *thread) +{ +#if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \ + && !defined(GC_WIN32_THREADS) && !defined(GC_DARWIN_THREADS) + return GC_is_thread_suspended (_Jv_GetPlatformThreadID (thread)); +#else + return 0; +#endif +} + void _Jv_GCAttachThread () { diff --git a/libjava/classpath/ChangeLog b/libjava/classpath/ChangeLog index 59e8953c7e5..3238e05e439 100644 --- a/libjava/classpath/ChangeLog +++ b/libjava/classpath/ChangeLog @@ -1,3 +1,24 @@ +2007-05-18 Keith Seitz <keiths@redhat.com> + + Merged from trunk: + + 2007-05-07 Keith Seitz <keiths@redhat.com> + * gnu/classpath/jdwp/Jdwp.java (notify): Rewrite to call + new array-based method. + (notify): New function. + (sendEvent): Rewrite to use sendEvents. + (sendEvents): New method. + * gnu/classpath/jdwp/event/Event.java (toPacket): Make static. + Change parameters to use arrays for events and requests. + Add suspendPolicy parameter. + Move per-event data transformation to... + (_toData): ... here. + * gnu/classpath/jdwp/transport/JdwpConnection.java + (sendEvent): Renamed to ... + (sendEvents): ... this. + Change parameters to use arrays for events and requests. + Add suspendPolicy parameter. + 2007-05-03 Andrew Haley <aph@redhat.com> * gnu/javax/management/Server.java (Server): Record the delegate. diff --git a/libjava/classpath/gnu/classpath/jdwp/Jdwp.java b/libjava/classpath/gnu/classpath/jdwp/Jdwp.java index e63a9a353dd..1d2329255cb 100644 --- a/libjava/classpath/gnu/classpath/jdwp/Jdwp.java +++ b/libjava/classpath/gnu/classpath/jdwp/Jdwp.java @@ -1,5 +1,5 @@ /* Jdwp.java -- Virtual machine to JDWP back-end programming interface - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -51,6 +51,7 @@ import gnu.classpath.jdwp.transport.TransportFactory; import java.io.IOException; import java.security.AccessController; +import java.util.ArrayList; import java.util.HashMap; /** @@ -207,23 +208,21 @@ public class Jdwp * The event is filtered through the event manager before being * sent. * - * FIXME: Probably need logic to send multiple events * @param event the event to report */ - public static void notify (Event event) + public static void notify(Event event) { - Jdwp jdwp = getDefault (); + Jdwp jdwp = getDefault(); if (jdwp != null) { - EventManager em = EventManager.getDefault (); - EventRequest request = em.getEventRequest (event); - if (request != null) + EventManager em = EventManager.getDefault(); + EventRequest[] requests = em.getEventRequests(event); + for (int i = 0; i < requests.length; ++i) { try { - System.out.println ("Jdwp.notify: sending event " + event); - sendEvent (request, event); - jdwp._enforceSuspendPolicy (request.getSuspendPolicy ()); + sendEvent(requests[i], event); + jdwp._enforceSuspendPolicy(requests[i].getSuspendPolicy()); } catch (Exception e) { @@ -236,6 +235,62 @@ public class Jdwp } /** + * Notify the debugger of "co-located" events. This method should + * not be called if debugging is not active (but it would not + * cause any harm). Places where event notifications occur + * should check isDebugging before doing anything. + * + * The events are filtered through the event manager before being + * sent. + * + * @param events the events to report + */ + public static void notify(Event[] events) + { + Jdwp jdwp = getDefault(); + + if (jdwp != null) + { + byte suspendPolicy = JdwpConstants.SuspendPolicy.NONE; + EventManager em = EventManager.getDefault(); + ArrayList allEvents = new ArrayList (); + ArrayList allRequests = new ArrayList (); + for (int i = 0; i < events.length; ++i) + { + EventRequest[] r = em.getEventRequests(events[i]); + for (int j = 0; j < r.length; ++j) + { + /* This is hacky, but it's not clear whether this + can really happen, and if it does, what should + occur. */ + allEvents.add (events[i]); + allRequests.add (r[j]); + + // Perhaps this is overkill? + if (r[j].getSuspendPolicy() > suspendPolicy) + suspendPolicy = r[j].getSuspendPolicy(); + } + } + + try + { + Event[] e = new Event[allEvents.size()]; + allEvents.toArray(e); + EventRequest[] r = new EventRequest[allRequests.size()]; + allRequests.toArray(r); + sendEvents(r, e, suspendPolicy); + jdwp._enforceSuspendPolicy(suspendPolicy); + } + catch (Exception e) + { + /* Really not much we can do. For now, just print out + a warning to the user. */ + System.out.println ("Jdwp.notify: caught exception: " + e); + } + } + } + + /** * Sends the event to the debugger. * * This method bypasses the event manager's filtering. @@ -247,13 +302,30 @@ public class Jdwp public static void sendEvent (EventRequest request, Event event) throws IOException { - Jdwp jdwp = getDefault (); + sendEvents (new EventRequest[] { request }, new Event[] { event }, + request.getSuspendPolicy()); + } + + /** + * Sends the events to the debugger. + * + * This method bypasses the event manager's filtering. + * + * @param requests list of debugger requests for the events + * @param events the events to send + * @param suspendPolicy the suspendPolicy enforced by the VM + * @throws IOException if a communications failure occurs + */ + public static void sendEvents (EventRequest[] requests, Event[] events, + byte suspendPolicy) + throws IOException + { + Jdwp jdwp = getDefault(); if (jdwp != null) { - // !! May need to implement send queue? synchronized (jdwp._connection) { - jdwp._connection.sendEvent (request, event); + jdwp._connection.sendEvents (requests, events, suspendPolicy); } } } diff --git a/libjava/classpath/gnu/classpath/jdwp/event/Event.java b/libjava/classpath/gnu/classpath/jdwp/event/Event.java index e91108a61a8..c89b25cb93b 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/Event.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/Event.java @@ -1,5 +1,5 @@ /* Event.java -- a base class for all event types - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -135,25 +135,30 @@ public abstract class Event public abstract Object getParameter (int type); /** - * Converts this event into to a JDWP packet + * Converts the events into to a single JDWP Event.COMPOSITE packet * * @param dos the stream to which to write data - * @param request the request the wanted this notification + * @param events the events to package into the packet + * @param requests the corresponding event requests + * @param suspendPolicy the suspend policy enforced by the VM * @returns a <code>JdwpPacket</code> of the events */ - public JdwpPacket toPacket (DataOutputStream dos, EventRequest request) + public static JdwpPacket toPacket (DataOutputStream dos, + Event[] events, + EventRequest[] requests, + byte suspendPolicy) { JdwpPacket pkt; try { - dos.writeByte (request.getSuspendPolicy ()); - dos.writeInt (1); - dos.writeByte (_eventKind); - dos.writeInt (request.getId ()); - _writeData (dos); - - pkt = new JdwpCommandPacket (JdwpConstants.CommandSet.Event.CS_VALUE, - JdwpConstants.CommandSet.Event.COMPOSITE); + dos.writeByte (suspendPolicy); + dos.writeInt (events.length); + for (int i = 0; i < events.length; ++i) + _toData (dos, events[i], requests[i]); + + pkt + = new JdwpCommandPacket (JdwpConstants.CommandSet.Event.CS_VALUE, + JdwpConstants.CommandSet.Event.COMPOSITE); } catch (IOException ioe) { @@ -162,4 +167,14 @@ public abstract class Event return pkt; } + + // Helper function for toPacket + private static void _toData (DataOutputStream dos, Event event, + EventRequest request) + throws IOException + { + dos.writeByte (event._eventKind); + dos.writeInt (request.getId ()); + event._writeData (dos); + } } diff --git a/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java b/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java index 54a7b08312d..aa3d5d6292c 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/EventManager.java @@ -1,5 +1,5 @@ /* EventManager.java -- event management and notification infrastructure - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -39,10 +39,12 @@ exception statement from your version. */ package gnu.classpath.jdwp.event; +import gnu.classpath.jdwp.Jdwp; import gnu.classpath.jdwp.VMVirtualMachine; import gnu.classpath.jdwp.exception.InvalidEventTypeException; import gnu.classpath.jdwp.exception.JdwpException; +import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; @@ -130,9 +132,10 @@ public class EventManager // only two: VM_INIT, VM_DEATH try { + byte sp = (Jdwp.suspendOnStartup() + ? EventRequest.SUSPEND_THREAD : EventRequest.SUSPEND_NONE); requestEvent (new EventRequest (0, - EventRequest.EVENT_VM_INIT, - EventRequest.SUSPEND_NONE)); + EventRequest.EVENT_VM_INIT, sp)); requestEvent (new EventRequest (0, EventRequest.EVENT_VM_DEATH, EventRequest.SUSPEND_NONE)); @@ -144,39 +147,39 @@ public class EventManager } /** - * Returns a request for the given event. This method will only + * Returns all requests for the given event. This method will only * be used if the <code>EventManager</code> is handling event filtering. * * @param event the event - * @return request that was interested in this event + * @return requests that are interested in this event * or <code>null</code> if none (and event should not be sent) * @throws IllegalArgumentException for invalid event kind */ - public EventRequest getEventRequest (Event event) + public EventRequest[] getEventRequests(Event event) { - EventRequest interestedRequest = null; + ArrayList interestedEvents = new ArrayList(); Hashtable requests; - Byte kind = new Byte (event.getEventKind ()); - requests = (Hashtable) _requests.get (kind); + Byte kind = new Byte(event.getEventKind()); + requests = (Hashtable) _requests.get(kind); if (requests == null) { // Did not get a valid event type - throw new IllegalArgumentException ("invalid event kind: " + kind); + throw new IllegalArgumentException("invalid event kind: " + kind); } - boolean match = false; // Loop through the requests. Must look at ALL requests in order // to evaluate all filters (think count filter). - // TODO: What if multiple matches? Spec isn't so clear on this. - Iterator rIter = requests.values().iterator (); - while (rIter.hasNext ()) + Iterator rIter = requests.values().iterator(); + while (rIter.hasNext()) { - EventRequest request = (EventRequest) rIter.next (); - if (request.matches (event)) - interestedRequest = request; + EventRequest request = (EventRequest) rIter.next(); + if (request.matches(event)) + interestedEvents.add(request); } - return interestedRequest; + EventRequest[] r = new EventRequest[interestedEvents.size()]; + interestedEvents.toArray(r); + return r; } /** diff --git a/libjava/classpath/gnu/classpath/jdwp/event/ThreadStartEvent.java b/libjava/classpath/gnu/classpath/jdwp/event/ThreadStartEvent.java index f9c507dfb16..4eff4409e8e 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/ThreadStartEvent.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/ThreadStartEvent.java @@ -1,6 +1,6 @@ /* ThreadStartEvent.java -- An event specifying that a new thread has started in the virtual machine - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -75,7 +75,7 @@ public class ThreadStartEvent * @param thread the thread ID in which event occurred */ public ThreadStartEvent (Thread thread) { - super (JdwpConstants.EventKind.THREAD_END); + super (JdwpConstants.EventKind.THREAD_START); _thread = thread; } diff --git a/libjava/classpath/gnu/classpath/jdwp/event/filters/LocationOnlyFilter.java b/libjava/classpath/gnu/classpath/jdwp/event/filters/LocationOnlyFilter.java index 7190317a4ae..a3125371c5d 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/filters/LocationOnlyFilter.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/filters/LocationOnlyFilter.java @@ -1,5 +1,5 @@ /* LocationOnlyFilter.java -- filter on location - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -49,13 +49,6 @@ import gnu.classpath.jdwp.util.Location; * May be used with breakpoint, field access, field modification, step, * and exception event kinds. * - * This "filter" is not really a filter. It is simply a way to communicate - * location information for supported events in a generic way to ease - * the burden of special casing several things in - * EventReqeustCommandSet.executeSet. - * - * Consequently, this "filter" always matches any event. - * * @author Keith Seitz (keiths@redhat.com) */ public class LocationOnlyFilter @@ -90,9 +83,12 @@ public class LocationOnlyFilter * * @param event the <code>Event</code> to scrutinize */ - public boolean matches (Event event) + public boolean matches(Event event) { - // This filter always matches. See comments in class javadoc. - return true; + Location loc = (Location) event.getParameter(Event.EVENT_LOCATION); + if (loc != null) + return (getLocation().equals(loc)); + + return false; } } diff --git a/libjava/classpath/gnu/classpath/jdwp/event/filters/StepFilter.java b/libjava/classpath/gnu/classpath/jdwp/event/filters/StepFilter.java index 340546e887d..d18f6975ebf 100644 --- a/libjava/classpath/gnu/classpath/jdwp/event/filters/StepFilter.java +++ b/libjava/classpath/gnu/classpath/jdwp/event/filters/StepFilter.java @@ -1,5 +1,5 @@ /* StepFilter.java -- a step filter - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.classpath.jdwp.event.filters; +import gnu.classpath.jdwp.JdwpConstants; import gnu.classpath.jdwp.event.Event; import gnu.classpath.jdwp.exception.InvalidThreadException; import gnu.classpath.jdwp.id.ThreadId; @@ -48,6 +49,12 @@ import gnu.classpath.jdwp.id.ThreadId; * satisfy depth and size constraints. This modifier can only be used with * step event kinds." * + * This "filter" is not really a filter. It is simply a way to communicate + * stepping information in a convenient way between the JDWP backend and + * the virtual machine. + * + * Consequently, this "filter" always matches. + * * @author Keith Seitz (keiths@redhat.com) */ public class StepFilter @@ -115,7 +122,6 @@ public class StepFilter */ public boolean matches (Event event) { - // FIXME - throw new RuntimeException ("StepFilter.matches not implemented"); + return true; } } diff --git a/libjava/classpath/gnu/classpath/jdwp/exception/AbsentInformationException.java b/libjava/classpath/gnu/classpath/jdwp/exception/AbsentInformationException.java new file mode 100644 index 00000000000..5bf383f5841 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/exception/AbsentInformationException.java @@ -0,0 +1,56 @@ +/* AbsentInformationException.java -- information not present exception + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.exception; + +import gnu.classpath.jdwp.JdwpConstants; + +/** + * An exception thrown when the requested information is not available. + * + * @author Kyle Galloway (kgallowa@redhat.com) + */ +public class AbsentInformationException + extends JdwpException +{ + public AbsentInformationException(String str) + { + super(JdwpConstants.Error.ABSENT_INFORMATION, str); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/exception/InvalidFrameException.java b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidFrameException.java new file mode 100644 index 00000000000..6d622a5e6f5 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidFrameException.java @@ -0,0 +1,63 @@ +/* InvalidFrameException.java -- invalid jframeid + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.exception; + +import gnu.classpath.jdwp.JdwpConstants; + +/** + * An exception thrown when the debugger requests an invalid frame in the call + * stack. + * + * @author Kyle Galloway (kgallowa@redhat.com) + */ +public class InvalidFrameException + extends JdwpException +{ + public InvalidFrameException(long id) + { + super(JdwpConstants.Error.INVALID_FRAMEID, + "invalid frame id (" + id + ")"); + } + + public InvalidFrameException(String msg) + { + super(JdwpConstants.Error.INVALID_FRAMEID, msg); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/exception/InvalidSlotException.java b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidSlotException.java new file mode 100644 index 00000000000..1827edab9a6 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidSlotException.java @@ -0,0 +1,62 @@ +/* InvalidSlotException.java -- an invalid variable slot exception + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.exception; + +import gnu.classpath.jdwp.JdwpConstants; + +/** + * An exception thrown when an invalid Slot id is used by the debugger + * (i.e. when trying to access a variable slot which doesn't exist). + * + * @author Kyle Galloway (kgallowa@redhat.com) + */ +public class InvalidSlotException + extends JdwpException +{ + public InvalidSlotException(int slot) + { + super(JdwpConstants.Error.INVALID_SLOT, "invalid slot: " + slot); + } + + public InvalidSlotException(String msg) + { + super(JdwpConstants.Error.INVALID_SLOT, msg); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/exception/InvalidTagException.java b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidTagException.java new file mode 100644 index 00000000000..738b5e7348b --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/exception/InvalidTagException.java @@ -0,0 +1,57 @@ +/* InvalidTagException.java -- an invalid type tag exception + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.exception; + +import gnu.classpath.jdwp.JdwpConstants; + +/** + * An exception thrown when an invalid tag is used by + * the debugger + * + * @author Kyle Galloway (kgallowa@redhat.com) + */ +public class InvalidTagException + extends JdwpException +{ + public InvalidTagException (byte tag) + { + super (JdwpConstants.Error.INVALID_TAG, + "invalid tag (" + tag + ")"); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/exception/TypeMismatchException.java b/libjava/classpath/gnu/classpath/jdwp/exception/TypeMismatchException.java new file mode 100644 index 00000000000..c6af65223d5 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/exception/TypeMismatchException.java @@ -0,0 +1,62 @@ +/* TypeMismatchException.java -- mismatched type of local variable + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.exception; + +import gnu.classpath.jdwp.JdwpConstants; + +/** + * An exception throw when attempting to access a local variable of the wrong + * type. + * + * @author Kyle Galloway (kgallowa@redhat.com) + */ +public class TypeMismatchException + extends JdwpException +{ + public TypeMismatchException(byte tag) + { + super(JdwpConstants.Error.TYPE_MISMATCH, "incorrect tag: " + tag); + } + + public TypeMismatchException(String msg) + { + super(JdwpConstants.Error.TYPE_MISMATCH, msg); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/id/NullObjectId.java b/libjava/classpath/gnu/classpath/jdwp/id/NullObjectId.java new file mode 100644 index 00000000000..0e7b5e39cd8 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/id/NullObjectId.java @@ -0,0 +1,79 @@ +/* NullObjectId.java -- special objectId for null values + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.id; + +import gnu.classpath.jdwp.exception.InvalidObjectException; +import gnu.classpath.jdwp.util.NullObject; + +import java.lang.ref.SoftReference; + +/** + * This is a special case of an ObjectId. When a varaible slot contains + * null as its value, this is a valid value despite the fact that it does + * not reference an object. To represent this, this will always be the id + * of the NullObject (0). + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public class NullObjectId + extends ObjectId +{ + /** + * The object class that this id represents + */ + public static final Class typeClass = NullObject.class; + + /** + * Constructs a new <code>NullObjectId</code> + */ + public NullObjectId() + { + super(); + setId((long) 0); + _reference = new SoftReference<NullObject>(new NullObject()); + try + { + disableCollection(); + } + catch(InvalidObjectException ex) + { + //This will not happen + } + } + +} diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/ArrayReferenceCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/ArrayReferenceCommandSet.java index a9dc6cc815f..0da0154d9d2 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/ArrayReferenceCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/ArrayReferenceCommandSet.java @@ -1,6 +1,6 @@ /* ArrayReferenceCommandSet.java -- class to implement the Array Reference Command Set - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -46,7 +46,8 @@ import gnu.classpath.jdwp.exception.JdwpException; import gnu.classpath.jdwp.exception.JdwpInternalErrorException; import gnu.classpath.jdwp.exception.NotImplementedException; import gnu.classpath.jdwp.id.ObjectId; -import gnu.classpath.jdwp.util.Value; +import gnu.classpath.jdwp.value.Value; +import gnu.classpath.jdwp.value.ValueFactory; import java.io.DataOutputStream; import java.io.IOException; @@ -150,11 +151,11 @@ public class ArrayReferenceCommandSet // tagged for (int i = first; i < first + length; i++) { - Object value = Array.get(array, i); + Value val = ValueFactory.createFromObject(Array.get(array, i), clazz); if (clazz.isPrimitive()) - Value.writeUntaggedValue(os, value); + val.writeUntagged(os); else - Value.writeTaggedValue(os, value); + val.writeTagged(os); } } @@ -168,7 +169,7 @@ public class ArrayReferenceCommandSet Class type = array.getClass().getComponentType(); for (int i = first; i < first + length; i++) { - Object value = Value.getUntaggedObj(bb, type); + Object value = Value.getUntaggedObject(bb, type); Array.set(array, i, value); } } diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/ClassTypeCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/ClassTypeCommandSet.java index a3a7ca05e59..b29b5710f91 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/ClassTypeCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/ClassTypeCommandSet.java @@ -1,6 +1,6 @@ /* ClassTypeCommandSet.java -- class to implement the ClassType Command Set - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -49,7 +49,8 @@ import gnu.classpath.jdwp.exception.NotImplementedException; import gnu.classpath.jdwp.id.ObjectId; import gnu.classpath.jdwp.id.ReferenceTypeId; import gnu.classpath.jdwp.util.MethodResult; -import gnu.classpath.jdwp.util.Value; +import gnu.classpath.jdwp.value.Value; +import gnu.classpath.jdwp.value.ValueFactory; import java.io.DataOutputStream; import java.io.IOException; @@ -128,7 +129,7 @@ public class ClassTypeCommandSet { ObjectId fieldId = idMan.readObjectId(bb); Field field = (Field) (fieldId.getObject()); - Object value = Value.getUntaggedObj(bb, field.getType()); + Object value = Value.getUntaggedObject(bb, field.getType()); try { field.setAccessible(true); // Might be a private field @@ -154,7 +155,8 @@ public class ClassTypeCommandSet Exception exception = mr.getThrownException(); ObjectId eId = idMan.getObjectId(exception); - Value.writeTaggedValue(os, value); + Value val = ValueFactory.createFromObject(value, mr.getResultType()); + val.writeTagged(os); eId.writeTagged(os); } @@ -192,7 +194,7 @@ public class ClassTypeCommandSet for (int i = 0; i < args; i++) { - values[i] = Value.getObj(bb); + values[i] = Value.getTaggedObject(bb); } int invokeOpts = bb.getInt(); @@ -207,6 +209,8 @@ public class ClassTypeCommandSet MethodResult mr = VMVirtualMachine.executeMethod(null, thread, clazz, method, values, false); + mr.setResultType(method.getReturnType()); + if (suspend) VMVirtualMachine.resumeAllThreads (); diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/EventRequestCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/EventRequestCommandSet.java index 59cfb94d39b..d7ebbc3a358 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/EventRequestCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/EventRequestCommandSet.java @@ -1,6 +1,6 @@ /* EventRequestCommandSet.java -- class to implement the EventRequest Command Set - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -40,6 +40,7 @@ exception statement from your version. */ package gnu.classpath.jdwp.processor; import gnu.classpath.jdwp.JdwpConstants; +import gnu.classpath.jdwp.VMVirtualMachine; import gnu.classpath.jdwp.event.EventManager; import gnu.classpath.jdwp.event.EventRequest; import gnu.classpath.jdwp.event.filters.ClassExcludeFilter; @@ -113,6 +114,28 @@ public class EventRequestCommandSet byte suspendPolicy = bb.get(); int modifiers = bb.getInt(); + switch (eventKind) + { + case JdwpConstants.EventKind.FIELD_ACCESS: + if (!VMVirtualMachine.canWatchFieldAccess) + { + String msg = "watching field accesses is not supported"; + throw new NotImplementedException(msg); + } + break; + + case JdwpConstants.EventKind.FIELD_MODIFICATION: + if (!VMVirtualMachine.canWatchFieldModification) + { + String msg = "watching field modifications is not supported"; + throw new NotImplementedException(msg); + } + break; + + default: + // okay + } + EventRequest eventReq = new EventRequest(eventKind, suspendPolicy); IEventFilter filter = null; ReferenceTypeId refId; diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/MethodCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/MethodCommandSet.java index dcfe7a6fc84..4d1bf709800 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/MethodCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/MethodCommandSet.java @@ -1,5 +1,5 @@ /* MethodCommandSet.java -- class to implement the Method Command Set - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -40,10 +40,11 @@ package gnu.classpath.jdwp.processor; import gnu.classpath.jdwp.JdwpConstants; import gnu.classpath.jdwp.VMMethod; +import gnu.classpath.jdwp.VMVirtualMachine; import gnu.classpath.jdwp.exception.JdwpException; import gnu.classpath.jdwp.exception.JdwpInternalErrorException; import gnu.classpath.jdwp.exception.NotImplementedException; -import gnu.classpath.jdwp.id.ClassReferenceTypeId; +import gnu.classpath.jdwp.id.ReferenceTypeId; import gnu.classpath.jdwp.util.LineTable; import gnu.classpath.jdwp.util.VariableTable; @@ -99,8 +100,7 @@ public class MethodCommandSet private void executeLineTable(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ClassReferenceTypeId refId - = (ClassReferenceTypeId) idMan.readReferenceTypeId(bb); + ReferenceTypeId refId = idMan.readReferenceTypeId(bb); Class clazz = refId.getType(); VMMethod method = VMMethod.readId(clazz, bb); @@ -111,8 +111,7 @@ public class MethodCommandSet private void executeVariableTable(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ClassReferenceTypeId refId - = (ClassReferenceTypeId) idMan.readReferenceTypeId(bb); + ReferenceTypeId refId = idMan.readReferenceTypeId(bb); Class clazz = refId.getType(); VMMethod method = VMMethod.readId(clazz, bb); @@ -121,11 +120,20 @@ public class MethodCommandSet } private void executeByteCodes(ByteBuffer bb, DataOutputStream os) - throws JdwpException + throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException("Command ByteCodes not implemented."); + if (!VMVirtualMachine.canGetBytecodes) + { + String msg = "getting bytecodes is unsupported"; + throw new NotImplementedException(msg); + } + + ReferenceTypeId id = idMan.readReferenceTypeId(bb); + Class klass = id.getType(); + VMMethod method = VMMethod.readId(klass, bb); + byte[] bytecode = VMVirtualMachine.getBytecodes(method); + os.writeInt(bytecode.length); + os.write(bytecode); } private void executeIsObsolete(ByteBuffer bb, DataOutputStream os) @@ -143,7 +151,7 @@ public class MethodCommandSet { // We don't have generics yet throw new NotImplementedException( - "Command SourceDebugExtension not implemented."); + "Command VariableTableWithGeneric not implemented."); } } diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/ObjectReferenceCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/ObjectReferenceCommandSet.java index ef421ea5b28..ed83fd2f94b 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/ObjectReferenceCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/ObjectReferenceCommandSet.java @@ -1,6 +1,6 @@ /* ObjectReferenceCommandSet.java -- class to implement the ObjectReference Command Set - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -47,8 +47,10 @@ import gnu.classpath.jdwp.exception.JdwpInternalErrorException; import gnu.classpath.jdwp.exception.NotImplementedException; import gnu.classpath.jdwp.id.ObjectId; import gnu.classpath.jdwp.id.ReferenceTypeId; -import gnu.classpath.jdwp.util.Value; import gnu.classpath.jdwp.util.MethodResult; +import gnu.classpath.jdwp.util.MonitorInfo; +import gnu.classpath.jdwp.value.Value; +import gnu.classpath.jdwp.value.ValueFactory; import java.io.DataOutputStream; import java.io.IOException; @@ -137,7 +139,9 @@ public class ObjectReferenceCommandSet { field.setAccessible(true); // Might be a private field Object value = field.get(obj); - Value.writeTaggedValue(os, value); + Value val = ValueFactory.createFromObject(value, + field.getType()); + val.writeTagged(os); } catch (IllegalArgumentException ex) { @@ -163,7 +167,7 @@ public class ObjectReferenceCommandSet for (int i = 0; i < numFields; i++) { Field field = (Field) idMan.readObjectId(bb).getObject(); - Object value = Value.getUntaggedObj(bb, field.getType()); + Object value = Value.getUntaggedObject(bb, field.getType()); try { field.setAccessible(true); // Might be a private field @@ -183,13 +187,18 @@ public class ObjectReferenceCommandSet } private void executeMonitorInfo(ByteBuffer bb, DataOutputStream os) - throws JdwpException + throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException( - "Command ExecuteMonitorInfo not implemented."); + if (!VMVirtualMachine.canGetMonitorInfo) + { + String msg = "getting monitor info not supported"; + throw new NotImplementedException(msg); + } + ObjectId oid = idMan.readObjectId(bb); + Object obj = oid.getObject(); + MonitorInfo info = VMVirtualMachine.getMonitorInfo(obj); + info.write(os); } private void executeInvokeMethod(ByteBuffer bb, DataOutputStream os) @@ -212,7 +221,7 @@ public class ObjectReferenceCommandSet for (int i = 0; i < args; i++) { - values[i] = Value.getObj(bb); + values[i] = Value.getTaggedObject(bb); } int invokeOptions = bb.getInt(); @@ -232,11 +241,14 @@ public class ObjectReferenceCommandSet MethodResult mr = VMVirtualMachine.executeMethod(obj, thread, clazz, method, values, nonVirtual); + mr.setResultType (method.getReturnType()); + Object value = mr.getReturnedValue(); Exception exception = mr.getThrownException(); ObjectId eId = idMan.getObjectId(exception); - Value.writeTaggedValue(os, value); + Value val = ValueFactory.createFromObject(value, mr.getResultType()); + val.writeTagged(os); eId.writeTagged(os); } diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/ReferenceTypeCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/ReferenceTypeCommandSet.java index b76143f92c9..c9b329869d7 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/ReferenceTypeCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/ReferenceTypeCommandSet.java @@ -1,6 +1,6 @@ /* ReferenceTypeCommandSet.java -- class to implement the ReferenceType Command Set - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -51,7 +51,8 @@ import gnu.classpath.jdwp.id.ObjectId; import gnu.classpath.jdwp.id.ReferenceTypeId; import gnu.classpath.jdwp.util.JdwpString; import gnu.classpath.jdwp.util.Signature; -import gnu.classpath.jdwp.util.Value; +import gnu.classpath.jdwp.value.Value; +import gnu.classpath.jdwp.value.ValueFactory; import java.io.DataOutputStream; import java.io.IOException; @@ -182,8 +183,7 @@ public class ReferenceTypeCommandSet private void executeMethods(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ClassReferenceTypeId refId - = (ClassReferenceTypeId) idMan.readReferenceTypeId(bb); + ReferenceTypeId refId = idMan.readReferenceTypeId(bb); Class clazz = refId.getType(); VMMethod[] methods = VMVirtualMachine.getAllClassMethods(clazz); @@ -220,7 +220,9 @@ public class ReferenceTypeCommandSet { field.setAccessible(true); // Might be a private field Object value = field.get(null); - Value.writeTaggedValue(os, value); + Value val = ValueFactory.createFromObject(value, + field.getType()); + val.writeTagged(os); } catch (IllegalArgumentException ex) { @@ -303,10 +305,15 @@ public class ReferenceTypeCommandSet private void executeSourceDebugExtension(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException( - "Command SourceDebugExtension not implemented."); + if (!VMVirtualMachine.canGetSourceDebugExtension) + { + String msg = "source debug extension is not supported"; + throw new NotImplementedException(msg); + } + + ReferenceTypeId id = idMan.readReferenceTypeId(bb); + String ext = VMVirtualMachine.getSourceDebugExtension (id.getType()); + JdwpString.writeString(os, ext); } private void executeSignatureWithGeneric(ByteBuffer bb, DataOutputStream os) @@ -314,7 +321,7 @@ public class ReferenceTypeCommandSet { // We don't have generics yet throw new NotImplementedException( - "Command SourceDebugExtension not implemented."); + "Command SignatureWithGeneric not implemented."); } private void executeFieldWithGeneric(ByteBuffer bb, DataOutputStream os) @@ -322,7 +329,7 @@ public class ReferenceTypeCommandSet { // We don't have generics yet throw new NotImplementedException( - "Command SourceDebugExtension not implemented."); + "Command executeFieldWithGeneric not implemented."); } private void executeMethodsWithGeneric(ByteBuffer bb, DataOutputStream os) @@ -330,6 +337,6 @@ public class ReferenceTypeCommandSet { // We don't have generics yet throw new NotImplementedException( - "Command SourceDebugExtension not implemented."); + "Command executeMethodsWithGeneric not implemented."); } } diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/StackFrameCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/StackFrameCommandSet.java index 7890a8e4b1b..2d90e8064ae 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/StackFrameCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/StackFrameCommandSet.java @@ -1,5 +1,5 @@ /* StackFrameCommandSet.java -- class to implement the StackFrame Command Set - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -45,8 +45,10 @@ import gnu.classpath.jdwp.VMVirtualMachine; import gnu.classpath.jdwp.exception.JdwpException; import gnu.classpath.jdwp.exception.JdwpInternalErrorException; import gnu.classpath.jdwp.exception.NotImplementedException; -import gnu.classpath.jdwp.id.ObjectId; -import gnu.classpath.jdwp.util.Value; +import gnu.classpath.jdwp.id.ThreadId; +import gnu.classpath.jdwp.value.ObjectValue; +import gnu.classpath.jdwp.value.Value; +import gnu.classpath.jdwp.value.ValueFactory; import java.io.DataOutputStream; import java.io.IOException; @@ -98,8 +100,8 @@ public class StackFrameCommandSet private void executeGetValues(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ObjectId tId = idMan.readObjectId(bb); - Thread thread = (Thread) tId.getObject(); + ThreadId tId = (ThreadId) idMan.readObjectId(bb); + Thread thread = tId.getThread(); // Although Frames look like other ids they are not. First they are not // ObjectIds since they don't exist in the users code. Storing them as an @@ -107,31 +109,33 @@ public class StackFrameCommandSet // has a reference to them. Furthermore they are not ReferenceTypeIds since // these are held permanently and we want these to be held only as long as // the Thread is suspended. - VMFrame frame = VMVirtualMachine.getFrame(thread, bb); + long frameID = bb.getLong(); + VMFrame frame = VMVirtualMachine.getFrame(thread, frameID); int slots = bb.getInt(); os.writeInt(slots); // Looks pointless but this is the protocol for (int i = 0; i < slots; i++) { int slot = bb.getInt(); byte sig = bb.get(); - Object val = frame.getValue(slot); - Value.writeTaggedValue(os, val); + Value val = frame.getValue(slot, sig); + val.writeTagged(os); } } private void executeSetValues(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ObjectId tId = idMan.readObjectId(bb); - Thread thread = (Thread) tId.getObject(); + ThreadId tId = (ThreadId) idMan.readObjectId(bb); + Thread thread = tId.getThread(); - VMFrame frame = VMVirtualMachine.getFrame(thread, bb); + long frameID = bb.getLong(); + VMFrame frame = VMVirtualMachine.getFrame(thread, frameID); int slots = bb.getInt(); for (int i = 0; i < slots; i++) { int slot = bb.getInt(); - Object value = Value.getObj(bb); + Value value = ValueFactory.createFromTagged(bb); frame.setValue(slot, value); } } @@ -139,20 +143,28 @@ public class StackFrameCommandSet private void executeThisObject(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - ObjectId tId = idMan.readObjectId(bb); - Thread thread = (Thread) tId.getObject(); + ThreadId tId = (ThreadId) idMan.readObjectId(bb); + Thread thread = tId.getThread(); - VMFrame frame = VMVirtualMachine.getFrame(thread, bb); + long frameID = bb.getLong(); + VMFrame frame = VMVirtualMachine.getFrame(thread, frameID); - Object thisObject = frame.getObject(); - Value.writeTaggedValue(os, thisObject); + ObjectValue objVal = new ObjectValue(frame.getObject()); + objVal.writeTagged(os); } private void executePopFrames(ByteBuffer bb, DataOutputStream os) - throws JdwpException + throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException("Command PopFrames not implemented."); + if (!VMVirtualMachine.canPopFrames) + { + String msg = "popping frames is unsupported"; + throw new NotImplementedException(msg); + } + + ThreadId tid = (ThreadId) idMan.readObjectId(bb); + Thread thread = tid.getThread(); + long fid = bb.getLong(); + VMVirtualMachine.popFrames(thread, fid); } } diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/ThreadReferenceCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/ThreadReferenceCommandSet.java index fd7fa743e7a..6fbcf698f8f 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/ThreadReferenceCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/ThreadReferenceCommandSet.java @@ -1,5 +1,5 @@ /* ThreadReferenceCommandSet.java -- class to implement the ThreadReference - Command Set Copyright (C) 2005 Free Software Foundation + Command Set Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -142,7 +142,7 @@ public class ThreadReferenceCommandSet { ThreadId tid = (ThreadId) idMan.readObjectId(bb); Thread thread = tid.getThread(); - VMVirtualMachine.suspendThread(thread); + VMVirtualMachine.resumeThread(thread); } private void executeStatus(ByteBuffer bb, DataOutputStream os) @@ -198,22 +198,42 @@ public class ThreadReferenceCommandSet } private void executeOwnedMonitors(ByteBuffer bb, DataOutputStream os) - throws JdwpException + throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException( - "Command OwnedMonitors not implemented."); + if (!VMVirtualMachine.canGetOwnedMonitorInfo) + { + String msg = "getting owned monitors is not supported"; + throw new NotImplementedException(msg); + } + + ThreadId tid = (ThreadId) idMan.readObjectId(bb); + Thread thread = tid.getThread(); + Object[] monitors = VMVirtualMachine.getOwnedMonitors(thread); + + os.write(monitors.length); + for (int i = 0; i < monitors.length; ++i) + { + ObjectId id = idMan.getObjectId(monitors[i]); + id.writeTagged(os); + } } private void executeCurrentContendedMonitor(ByteBuffer bb, DataOutputStream os) - throws JdwpException + throws JdwpException, IOException { - // This command is optional, determined by VirtualMachines CapabilitiesNew - // so we'll leave it till later to implement - throw new NotImplementedException( - "Command CurrentContentedMonitors not implemented."); + if (!VMVirtualMachine.canGetCurrentContendedMonitor) + { + String msg = "getting current contended monitor is not supported"; + throw new NotImplementedException(msg); + } + + ThreadId tid = (ThreadId) idMan.readObjectId(bb); + Thread thread = tid.getThread(); + + Object monitor = VMVirtualMachine.getCurrentContendedMonitor(thread); + ObjectId id = idMan.getObjectId(monitor); + id.writeTagged(os); } private void executeStop(ByteBuffer bb, DataOutputStream os) diff --git a/libjava/classpath/gnu/classpath/jdwp/processor/VirtualMachineCommandSet.java b/libjava/classpath/gnu/classpath/jdwp/processor/VirtualMachineCommandSet.java index a7edb287a56..e2703908ba2 100644 --- a/libjava/classpath/gnu/classpath/jdwp/processor/VirtualMachineCommandSet.java +++ b/libjava/classpath/gnu/classpath/jdwp/processor/VirtualMachineCommandSet.java @@ -1,6 +1,6 @@ /* VirtualMachineCommandSet.java -- class to implement the VirtualMachine Command Set - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -54,6 +54,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.Properties; @@ -179,7 +180,8 @@ public class VirtualMachineCommandSet ArrayList allMatchingClasses = new ArrayList(); // This will be an Iterator over all loaded Classes - Iterator iter = VMVirtualMachine.getAllLoadedClasses(); + Collection classes = VMVirtualMachine.getAllLoadedClasses(); + Iterator iter = classes.iterator (); while (iter.hasNext()) { @@ -203,22 +205,11 @@ public class VirtualMachineCommandSet private void executeAllClasses(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - // Disable garbage collection while we're collecting the info on loaded - // classes so we some classes don't get collected between the time we get - // the count and the time we get the list - //VMVirtualMachine.disableGarbageCollection(); + Collection classes = VMVirtualMachine.getAllLoadedClasses(); + os.writeInt(classes.size ()); - int classCount = VMVirtualMachine.getAllLoadedClassesCount(); - os.writeInt(classCount); - - // This will be an Iterator over all loaded Classes - Iterator iter = VMVirtualMachine.getAllLoadedClasses(); - //VMVirtualMachine.enableGarbageCollection(); - int count = 0; - - // Note it's possible classes were created since out classCount so make - // sure we don't write more classes than we told the debugger - while (iter.hasNext() && count++ < classCount) + Iterator iter = classes.iterator (); + while (iter.hasNext()) { Class clazz = (Class) iter.next(); ReferenceTypeId id = idMan.getReferenceTypeId(clazz); @@ -340,14 +331,13 @@ public class VirtualMachineCommandSet private void executeCapabilities(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - // Store these somewhere? - os.writeBoolean(false); // canWatchFieldModification - os.writeBoolean(false); // canWatchFieldAccess - os.writeBoolean(false); // canGetBytecodes - os.writeBoolean(false); // canGetSyntheticAttribute - os.writeBoolean(false); // canGetOwnedMonitorInfo - os.writeBoolean(false); // canGetCurrentContendedMonitor - os.writeBoolean(false); // canGetMonitorInfo + os.writeBoolean(VMVirtualMachine.canWatchFieldModification); + os.writeBoolean(VMVirtualMachine.canWatchFieldAccess); + os.writeBoolean(VMVirtualMachine.canGetBytecodes); + os.writeBoolean(VMVirtualMachine.canGetSyntheticAttribute); + os.writeBoolean(VMVirtualMachine.canGetOwnedMonitorInfo); + os.writeBoolean(VMVirtualMachine.canGetCurrentContendedMonitor); + os.writeBoolean(VMVirtualMachine.canGetMonitorInfo); } private void executeClassPaths(ByteBuffer bb, DataOutputStream os) @@ -401,43 +391,60 @@ public class VirtualMachineCommandSet private void executeCapabilitiesNew(ByteBuffer bb, DataOutputStream os) throws JdwpException, IOException { - // Store these somewhere? final int CAPABILITIES_NEW_SIZE = 32; - os.writeBoolean(false); // canWatchFieldModification - os.writeBoolean(false); // canWatchFieldAccess - os.writeBoolean(false); // canGetBytecodes - os.writeBoolean(false); // canGetSyntheticAttribute - os.writeBoolean(false); // canGetOwnedMonitorInfo - os.writeBoolean(false); // canGetCurrentContendedMonitor - os.writeBoolean(false); // canGetMonitorInfo - os.writeBoolean(false); // canRedefineClasses - os.writeBoolean(false); // canAddMethod - os.writeBoolean(false); // canUnrestrictedlyRedefineClasses - os.writeBoolean(false); // canPopFrames - os.writeBoolean(false); // canUseInstanceFilters - os.writeBoolean(false); // canGetSourceDebugExtension - os.writeBoolean(false); // canRequestVMDeathEvent - os.writeBoolean(false); // canSetDefaultStratum + + executeCapabilities(bb, os); + os.writeBoolean(VMVirtualMachine.canRedefineClasses); + os.writeBoolean(VMVirtualMachine.canAddMethod); + os.writeBoolean(VMVirtualMachine.canUnrestrictedlyRedefineClasses); + os.writeBoolean(VMVirtualMachine.canPopFrames); + os.writeBoolean(VMVirtualMachine.canUseInstanceFilters); + os.writeBoolean(VMVirtualMachine.canGetSourceDebugExtension); + os.writeBoolean(VMVirtualMachine.canRequestVMDeathEvent); + os.writeBoolean(VMVirtualMachine.canSetDefaultStratum); for (int i = 15; i < CAPABILITIES_NEW_SIZE; i++) - // Future capabilities - // currently unused - os.writeBoolean(false); // Set to false + { + // Future capabilities (currently unused) + os.writeBoolean(false); + } } private void executeRedefineClasses(ByteBuffer bb, DataOutputStream os) throws JdwpException { - // Optional command, don't implement - throw new NotImplementedException( - "Command VirtualMachine.RedefineClasses not implemented"); + if (!VMVirtualMachine.canRedefineClasses) + { + String msg = "redefinition of classes is not supported"; + throw new NotImplementedException(msg); + } + + int classes = bb.getInt(); + Class[] types = new Class[classes]; + byte[][] bytecodes = new byte[classes][]; + for (int i = 0; i < classes; ++i) + { + ReferenceTypeId id = idMan.readReferenceTypeId(bb); + int classfile = bb.getInt(); + byte[] bytecode = new byte[classfile]; + bb.get(bytecode); + types[i] = id.getType(); + bytecodes[i] = bytecode; + } + + VMVirtualMachine.redefineClasses (types, bytecodes); } private void executeSetDefaultStratum(ByteBuffer bb, DataOutputStream os) throws JdwpException { - // Optional command, don't implement - throw new NotImplementedException( - "Command VirtualMachine.SetDefaultStratum not implemented"); + if (!VMVirtualMachine.canSetDefaultStratum) + { + String msg = "setting the default stratum is not supported"; + throw new NotImplementedException(msg); + } + + String stratum = JdwpString.readString(bb); + VMVirtualMachine.setDefaultStratum(stratum); } private void executeAllClassesWithGeneric(ByteBuffer bb, DataOutputStream os) diff --git a/libjava/classpath/gnu/classpath/jdwp/transport/JdwpConnection.java b/libjava/classpath/gnu/classpath/jdwp/transport/JdwpConnection.java index 82a2380bb7b..44158aa26ec 100644 --- a/libjava/classpath/gnu/classpath/jdwp/transport/JdwpConnection.java +++ b/libjava/classpath/gnu/classpath/jdwp/transport/JdwpConnection.java @@ -1,5 +1,5 @@ /* JdwpConnection.java -- A JDWP-speaking connection - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -267,13 +267,17 @@ public class JdwpConnection } /** - * Send an event notification to the debugger + * Send an event notification to the debugger. Note that this + * method will only send out one notification: all the events + * are passed in a single Event.COMPOSITE packet. * - * @param request the debugger request that wanted this event - * @param event the event + * @param requests debugger requests for events + * @param events the events to send + * @param suspendPolicy the suspend policy enforced by the VM * @throws IOException */ - public void sendEvent (EventRequest request, Event event) + public void sendEvents(EventRequest[] requests, Event[] events, + byte suspendPolicy) throws IOException { JdwpPacket pkt; @@ -281,7 +285,7 @@ public class JdwpConnection synchronized (_bytes) { _bytes.reset (); - pkt = event.toPacket (_doStream, request); + pkt = Event.toPacket (_doStream, events, requests, suspendPolicy); pkt.setData (_bytes.toByteArray ()); } diff --git a/libjava/classpath/gnu/classpath/jdwp/transport/SocketTransport.java b/libjava/classpath/gnu/classpath/jdwp/transport/SocketTransport.java index 49d9e1f3bf6..3b0a8e7fe12 100644 --- a/libjava/classpath/gnu/classpath/jdwp/transport/SocketTransport.java +++ b/libjava/classpath/gnu/classpath/jdwp/transport/SocketTransport.java @@ -1,5 +1,5 @@ /* SocketTransport.java -- a socket transport - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -89,27 +89,36 @@ class SocketTransport * @param properties the properties of the JDWP session * @throws TransportException for any configury errors */ - public void configure (HashMap properties) + public void configure(HashMap properties) throws TransportException { - // Get address [form: "hostname:port"] - String p = (String) properties.get (_PROPERTY_ADDRESS); + // Get server [form: "y" or "n"] + String p = (String) properties.get(_PROPERTY_SERVER); if (p != null) { - String[] s = p.split (":"); - if (s.length == 2) - { - _host = s[0]; - _port = Integer.parseInt (s[1]); - } + if (p.toLowerCase().equals("y")) + _server = true; } - // Get server [form: "y" or "n"] - p = (String) properties.get (_PROPERTY_SERVER); + // Get address [form: "hostname:port"] + p = (String) properties.get(_PROPERTY_ADDRESS); if (p != null) { - if (p.toLowerCase().equals ("y")) - _server = true; + String[] s = p.split(":"); + if (s.length == 1) + { + // Port number only. Assume "localhost" + _port = Integer.parseInt(s[0]); + _host = "localhost"; + } + else + { + if (s[0].length() == 0) + _host = "localhost"; + else + _host = s[0]; + _port = Integer.parseInt(s[1]); + } } } diff --git a/libjava/classpath/gnu/classpath/jdwp/util/Location.java b/libjava/classpath/gnu/classpath/jdwp/util/Location.java index 89e81e563a1..ff045a5eccc 100644 --- a/libjava/classpath/gnu/classpath/jdwp/util/Location.java +++ b/libjava/classpath/gnu/classpath/jdwp/util/Location.java @@ -1,5 +1,5 @@ /* Location.java -- class to read/write JDWP locations - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -153,4 +153,16 @@ public class Location { return method.toString () + "." + index; } + + public boolean equals(Object obj) + { + if (obj instanceof Location) + { + Location l = (Location) obj; + return (getMethod().equals(l.getMethod()) + && getIndex() == l.getIndex()); + } + + return false; + } } diff --git a/libjava/classpath/gnu/classpath/jdwp/util/MethodResult.java b/libjava/classpath/gnu/classpath/jdwp/util/MethodResult.java index a9c1b330579..190511de83f 100644 --- a/libjava/classpath/gnu/classpath/jdwp/util/MethodResult.java +++ b/libjava/classpath/gnu/classpath/jdwp/util/MethodResult.java @@ -52,6 +52,9 @@ public class MethodResult // Any Exception that was thrown by the executing method private Exception thrownException; + + // The type of this result + private Class resType; public Object getReturnedValue() { @@ -73,4 +76,14 @@ public class MethodResult this.thrownException = thrownException; } + public Class getResultType() + { + return resType; + } + + public void setResultType(Class type) + { + resType = type; + } + } diff --git a/libjava/classpath/gnu/classpath/jdwp/util/MonitorInfo.java b/libjava/classpath/gnu/classpath/jdwp/util/MonitorInfo.java new file mode 100644 index 00000000000..f28eabf834e --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/util/MonitorInfo.java @@ -0,0 +1,76 @@ +/* MonitorInfo.java -- class used to return monitor information + for JDWP. + + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.util; + +import gnu.classpath.jdwp.VMIdManager; +import gnu.classpath.jdwp.id.ObjectId; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * This class is used to pass monitor information between + * the JDWP back-end and the virtual machine. + * + * @author Keith Seitz (keiths@redhat.com) + */ +public class MonitorInfo +{ + public int entryCount; + public Thread owner; + public Thread[] waiters; + + public void write(DataOutputStream os) + throws IOException + { + VMIdManager idm = VMIdManager.getDefault(); + ObjectId id = idm.getObjectId(owner); + id.write(os); + os.write(entryCount); + os.write(waiters.length); + for (int i = 0; i < waiters.length; ++i) + { + id = idm.getObjectId(waiters[i]); + id.write(os); + } + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/util/NullObject.java b/libjava/classpath/gnu/classpath/jdwp/util/NullObject.java new file mode 100644 index 00000000000..ec762fc2fbf --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/util/NullObject.java @@ -0,0 +1,50 @@ +/* NullObject.java -- placeholder for null values + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.util; + +/** + * This is a placeholder for null. There are several places in JDWP where null + * is a valid value (i.e. when geting the value of a variable slot that + * contains a null reference at that time). This class distinguishes between + * these "meaningful" null values and invalid null pointers. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public class NullObject +{ +} diff --git a/libjava/classpath/gnu/classpath/jdwp/util/Value.java b/libjava/classpath/gnu/classpath/jdwp/util/Value.java index 414c4a33b82..e69de29bb2d 100644 --- a/libjava/classpath/gnu/classpath/jdwp/util/Value.java +++ b/libjava/classpath/gnu/classpath/jdwp/util/Value.java @@ -1,301 +0,0 @@ -/* Value.java -- class to read/write JDWP tagged and untagged values - Copyright (C) 2005, 2006, Free Software Foundation - -This file is part of GNU Classpath. - -GNU Classpath is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Classpath 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 for more details. - -You should have received a copy of the GNU General Public License -along with GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - - -package gnu.classpath.jdwp.util; - -import gnu.classpath.jdwp.JdwpConstants; -import gnu.classpath.jdwp.VMIdManager; -import gnu.classpath.jdwp.exception.InvalidFieldException; -import gnu.classpath.jdwp.exception.JdwpException; -import gnu.classpath.jdwp.exception.JdwpInternalErrorException; -import gnu.classpath.jdwp.exception.NotImplementedException; -import gnu.classpath.jdwp.id.ObjectId; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * A class to read/write JDWP tagged and untagged values. - * - * @author Aaron Luchko <aluchko@redhat.com> - */ -public class Value -{ - /** - * Will write the given object as an untagged value to the DataOutputStream. - * - * @param os write the value here - * @param obj the Object to write - * @throws IOException - * @throws InvalidFieldException - */ - public static void writeUntaggedValue(DataOutputStream os, Object obj) - throws JdwpException, IOException - { - writeValue(os, obj, false); - } - - /** - * Will write the given object as a tagged value to the DataOutputStream. - * - * @param os write the value here - * @param obj the Object to write - * @throws IOException - * @throws InvalidFieldException - */ - public static void writeTaggedValue(DataOutputStream os, Object obj) - throws JdwpException, IOException - { - writeValue(os, obj, true); - } - - /** - * Will write the given object as either a value or an untagged value to the - * DataOutputStream. - * - * @param os write the value here - * @param obj the Object to write - * @param tagged true if the value is tagged, false otherwise - * @throws IOException - * @throws InvalidFieldException - */ - private static void writeValue(DataOutputStream os, Object obj, - boolean tagged) - throws IOException, JdwpException - { - Class clazz = obj.getClass(); - if (clazz.isPrimitive()) - { - if (clazz == byte.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.BYTE); - os.writeByte(((Byte) obj).byteValue()); - } - else if (clazz == char.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.CHAR); - os.writeChar(((Character) obj).charValue()); - } - else if (clazz == float.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.FLOAT); - os.writeFloat(((Float) obj).floatValue()); - } - else if (clazz == double.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.DOUBLE); - os.writeDouble(((Double) obj).doubleValue()); - } - else if (clazz == int.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.BYTE); - os.writeInt(((Integer) obj).intValue()); - } - else if (clazz == long.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.LONG); - os.writeLong(((Long) obj).longValue()); - } - else if (clazz == short.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.SHORT); - os.writeInt(((Short) obj).shortValue()); - } - else if (clazz == void.class) - { // A 'void' has no data - if (tagged) - os.writeByte(JdwpConstants.Tag.VOID); - } - else if (clazz == boolean.class) - { - if (tagged) - os.writeByte(JdwpConstants.Tag.BOOLEAN); - os.writeBoolean(((Boolean) obj).booleanValue()); - } - else - { // This shouldn't be possible - throw new JdwpInternalErrorException( - "Field has invalid primitive!"); - } - } - else - { - // Object is an Object, not a primitive type wrapped in an object - // Write the appropriate tag - if (tagged) - { - if (clazz.isArray()) - os.writeByte(JdwpConstants.Tag.ARRAY); - else if (obj instanceof String) - os.writeByte(JdwpConstants.Tag.STRING); - else if (obj instanceof Thread) - os.writeByte(JdwpConstants.Tag.THREAD); - else if (obj instanceof ThreadGroup) - os.writeByte(JdwpConstants.Tag.THREAD_GROUP); - else if (obj instanceof ClassLoader) - os.writeByte(JdwpConstants.Tag.CLASS_LOADER); - else if (obj instanceof Class) - os.writeByte(JdwpConstants.Tag.CLASS_OBJECT); - else - os.writeByte(JdwpConstants.Tag.OBJECT); - } - ObjectId oid = VMIdManager.getDefault().getObjectId(obj); - oid.write(os); - } - } - - /** - * Reads the appropriate object for the tagged value contained in the - * ByteBuffer. - * - * @param bb contains the Object - * @return The Object referenced by the value - * @throws JdwpException - * @throws IOException - */ - public static Object getObj(ByteBuffer bb) - throws JdwpException, IOException - { - return getUntaggedObj(bb, bb.get()); - } - - /** - * Reads an object of the given Class from the untagged value contained - * in the ByteBuffer. - * - * @param bb contains the Object - * @param type corresponds to the TAG of value to be read - * @return the resultant object - * @throws JdwpException - * @throws IOException - */ - public static Object getUntaggedObj(ByteBuffer bb, Class type) - throws JdwpException, IOException - { - if (type.isPrimitive()) - { - if (type == byte.class) - return new Byte(bb.get()); - else if (type == char.class) - return new Character(bb.getChar()); - else if (type == float.class) - return new Float(bb.getFloat()); - else if (type == double.class) - return new Double(bb.getDouble()); - else if (type == int.class) - return new Integer(bb.getInt()); - else if (type == long.class) - return new Long(bb.getLong()); - else if (type == short.class) - return new Short(bb.getShort()); - else if (type == boolean.class) - return Boolean.valueOf(bb.get() != 0); - else if (type == void.class) - return new byte[0]; - else - { // This shouldn't be possible - throw new JdwpInternalErrorException( - "Field has invalid primitive!"); - } - } - else - { - // Field is an object - ObjectId oid = VMIdManager.getDefault().readObjectId(bb); - return oid.getObject(); - } - } - - /** - * Reads the an object of the given Class from the untagged value contained - * in the ByteBuffer. - * - * @param bb contains the Object - * @param tag TAG of the Value to be read - * @return the object - * @throws JdwpException - * @throws IOException - */ - public static Object getUntaggedObj(ByteBuffer bb, byte tag) - throws JdwpException, IOException - { - switch (tag) - { - case JdwpConstants.Tag.BYTE: - return new Byte(bb.get()); - case JdwpConstants.Tag.CHAR: - return new Character(bb.getChar()); - case JdwpConstants.Tag.FLOAT: - return new Float(bb.getFloat()); - case JdwpConstants.Tag.DOUBLE: - return new Double(bb.getDouble()); - case JdwpConstants.Tag.INT: - return new Integer(bb.getInt()); - case JdwpConstants.Tag.LONG: - return new Long(bb.getLong()); - case JdwpConstants.Tag.SHORT: - return new Short(bb.getShort()); - case JdwpConstants.Tag.VOID: - return new byte[0]; - case JdwpConstants.Tag.BOOLEAN: - return (bb.get() == 0) ? new Boolean(false) : new Boolean(true); - case JdwpConstants.Tag.STRING: - return JdwpString.readString(bb); - case JdwpConstants.Tag.ARRAY: - case JdwpConstants.Tag.THREAD: - case JdwpConstants.Tag.OBJECT: - case JdwpConstants.Tag.THREAD_GROUP: - case JdwpConstants.Tag.CLASS_LOADER: - case JdwpConstants.Tag.CLASS_OBJECT: - // All these cases are ObjectIds - ObjectId oid = VMIdManager.getDefault().readObjectId(bb); - return oid.getObject(); - default: - throw new NotImplementedException("Tag " + tag - + " is not implemented."); - } - } -} diff --git a/libjava/classpath/gnu/classpath/jdwp/util/VariableTable.java b/libjava/classpath/gnu/classpath/jdwp/util/VariableTable.java index 22d8c7dd621..f30c2158100 100644 --- a/libjava/classpath/gnu/classpath/jdwp/util/VariableTable.java +++ b/libjava/classpath/gnu/classpath/jdwp/util/VariableTable.java @@ -50,9 +50,9 @@ import java.io.IOException; public class VariableTable { - private final long argCnt; + private final int argCnt; - private final long slots; + private final int slots; private final long[] lineCI; @@ -95,8 +95,8 @@ public class VariableTable */ public void write(DataOutputStream os) throws IOException { - os.writeLong(argCnt); - os.writeLong(slots); + os.writeInt(argCnt); + os.writeInt(slots); for (int i = 0; i < slots; i++) { os.writeLong(lineCI[i]); diff --git a/libjava/classpath/gnu/classpath/jdwp/value/ArrayValue.java b/libjava/classpath/gnu/classpath/jdwp/value/ArrayValue.java new file mode 100644 index 00000000000..c5e00a019d9 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/ArrayValue.java @@ -0,0 +1,92 @@ +/* ArrayValue.java -- JDWP wrapper class for an Object value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; +import gnu.classpath.jdwp.VMIdManager; +import gnu.classpath.jdwp.id.ObjectId; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an Array value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public class ArrayValue + extends Value +{ + // The Array wrapped by this class represented as a Object + Object _value; + + /** + * Create a new ArrayValue from an Object + * + * @param value the Object to wrap + */ + public ArrayValue(Object value) + { + super(JdwpConstants.Tag.ARRAY); + _value = value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return _value; + } + + /** + * Write the wrapped object to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + ObjectId oid = VMIdManager.getDefault().getObjectId(_value); + oid.write(os); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/BooleanValue.java b/libjava/classpath/gnu/classpath/jdwp/value/BooleanValue.java new file mode 100644 index 00000000000..42bb806fadb --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/BooleanValue.java @@ -0,0 +1,100 @@ +/* BooleanValue.java -- JDWP wrapper class for a boolean value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an boolean value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class BooleanValue + extends Value +{ + // The boolean wrapped by this class + boolean _value; + + /** + * Create a new BooleanValue from an boolean + * + * @param value the boolean to wrap + */ + public BooleanValue(boolean value) + { + super(JdwpConstants.Tag.BOOLEAN); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public boolean getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Boolean(_value); + } + + /** + * Write the wrapped boolean to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeBoolean(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/ByteValue.java b/libjava/classpath/gnu/classpath/jdwp/value/ByteValue.java new file mode 100644 index 00000000000..204fe86b0e8 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/ByteValue.java @@ -0,0 +1,99 @@ +/* ByteValue.java -- JDWP wrapper class for a byte value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an byte value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class ByteValue + extends Value +{ + // The byte wrapped by this class + byte _value; + + /** + * Create a new ByteValue from an byte + * + * @param value the byte to wrap + */ + public ByteValue(byte value) + { + super(JdwpConstants.Tag.BYTE); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public byte getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Byte(_value); + } + + /** + * Write the wrapped byte to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeByte(_value); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/CharValue.java b/libjava/classpath/gnu/classpath/jdwp/value/CharValue.java new file mode 100644 index 00000000000..f68fc88d156 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/CharValue.java @@ -0,0 +1,100 @@ +/* CharValue.java -- JDWP wrapper class for a char value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an char value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class CharValue + extends Value +{ + // The char wrapped by this class + char _value; + + /** + * Create a new CharValue from an char + * + * @param value the char to wrap + */ + public CharValue(char value) + { + super(JdwpConstants.Tag.CHAR); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public char getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Character(_value); + } + + /** + * Write the wrapped char to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeChar(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/DoubleValue.java b/libjava/classpath/gnu/classpath/jdwp/value/DoubleValue.java new file mode 100644 index 00000000000..45923c4dac4 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/DoubleValue.java @@ -0,0 +1,100 @@ +/* DoubleValue.java -- JDWP wrapper class for a double value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an double value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class DoubleValue + extends Value +{ + // The double wrapped by this class + double _value; + + /** + * Create a new DoubleValue from an double + * + * @param value the double to wrap + */ + public DoubleValue(double value) + { + super(JdwpConstants.Tag.DOUBLE); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public double getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Double(_value); + } + + /** + * Write the wrapped double to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeDouble(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/FloatValue.java b/libjava/classpath/gnu/classpath/jdwp/value/FloatValue.java new file mode 100644 index 00000000000..970f77c07b4 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/FloatValue.java @@ -0,0 +1,100 @@ +/* FloatValue.java -- JDWP wrapper class for a float value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +afloat with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an float value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class FloatValue + extends Value +{ + // The float wrapped by this class + float _value; + + /** + * Create a new FloatValue from an float + * + * @param value the float to wrap + */ + public FloatValue(float value) + { + super(JdwpConstants.Tag.FLOAT); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public float getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Float(_value); + } + + /** + * Write the wrapped float to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeFloat(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/IntValue.java b/libjava/classpath/gnu/classpath/jdwp/value/IntValue.java new file mode 100644 index 00000000000..27868754f7f --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/IntValue.java @@ -0,0 +1,100 @@ +/* IntValue.java -- JDWP wrapper class for an int value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an int value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class IntValue + extends Value +{ + // The int wrapped by this class + int _value; + + /** + * Create a new IntValue from an int + * + * @param value the int to wrap + */ + public IntValue(int value) + { + super(JdwpConstants.Tag.INT); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public int getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Integer(_value); + } + + /** + * Write the wrapped int to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeInt(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/LongValue.java b/libjava/classpath/gnu/classpath/jdwp/value/LongValue.java new file mode 100644 index 00000000000..0ebe0e9f98a --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/LongValue.java @@ -0,0 +1,100 @@ +/* LongValue.java -- JDWP wrapper class for a long value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an long value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class LongValue + extends Value +{ + // The long wrapped by this class + long _value; + + /** + * Create a new LongValue from an long + * + * @param value the long to wrap + */ + public LongValue(long value) + { + super(JdwpConstants.Tag.LONG); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public long getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Long(_value); + } + + /** + * Write the wrapped long to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeLong(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/ObjectValue.java b/libjava/classpath/gnu/classpath/jdwp/value/ObjectValue.java new file mode 100644 index 00000000000..7ec9beb5f93 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/ObjectValue.java @@ -0,0 +1,92 @@ +/* ObjectValue.java -- JDWP wrapper class for an Object value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; +import gnu.classpath.jdwp.VMIdManager; +import gnu.classpath.jdwp.id.ObjectId; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an Object value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class ObjectValue + extends Value +{ + // The Object wrapped by this class + Object _value; + + /** + * Create a new ObjectValue from an Object + * + * @param value the Object to wrap + */ + public ObjectValue(Object value) + { + super(JdwpConstants.Tag.OBJECT); + _value = value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return _value; + } + + /** + * Write the wrapped object to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + ObjectId oid = VMIdManager.getDefault().getObjectId(_value); + oid.write(os); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/ShortValue.java b/libjava/classpath/gnu/classpath/jdwp/value/ShortValue.java new file mode 100644 index 00000000000..cbde2269e6c --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/ShortValue.java @@ -0,0 +1,100 @@ +/* ShortValue.java -- JDWP wrapper class for a short value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an short value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class ShortValue + extends Value +{ + // The short wrapped by this class + short _value; + + /** + * Create a new ShortValue from a short + * + * @param value the short to wrap + */ + public ShortValue(short value) + { + super(JdwpConstants.Tag.SHORT); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public short getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return new Short(_value); + } + + /** + * Write the wrapped short to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + os.writeShort(_value); + } +} + diff --git a/libjava/classpath/gnu/classpath/jdwp/value/StringValue.java b/libjava/classpath/gnu/classpath/jdwp/value/StringValue.java new file mode 100644 index 00000000000..2371547efed --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/StringValue.java @@ -0,0 +1,100 @@ +/* StringValue.java -- JDWP wrapper class for an String value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; +import gnu.classpath.jdwp.util.JdwpString; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an String value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public final class StringValue + extends Value +{ + // The String wrapped by this class + String _value; + + /** + * Create a new StringValue from an String + * + * @param value the String to wrap + */ + public StringValue(String value) + { + super(JdwpConstants.Tag.OBJECT); + _value = value; + } + + /** + * Get the value held in this Value + * + * @return the value represented by this Value object + */ + public String getValue() + { + return _value; + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return _value; + } + + /** + * Write the wrapped object to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + JdwpString.writeString(os, _value); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/Value.java b/libjava/classpath/gnu/classpath/jdwp/value/Value.java new file mode 100644 index 00000000000..4ad8bec0703 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/Value.java @@ -0,0 +1,155 @@ +/* Value.java -- base class of JDWP values + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.exception.InvalidClassException; +import gnu.classpath.jdwp.exception.InvalidObjectException; +import gnu.classpath.jdwp.exception.InvalidTagException; +import gnu.classpath.jdwp.exception.JdwpInternalErrorException; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Superclass for all JDWP Values. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public abstract class Value +{ + // A Tag representing the type of this value + private byte _tag; + + /** + * Create a new value of type tag. + * + * @param tag the type of the value + */ + protected Value(byte tag) + { + _tag = tag; + } + + /** + * Get the tag for this Value + * + * @return the byte tag of this Value + */ + public byte getTag() + { + return _tag; + } + + /** + * Calls the dervied classes writeValue method to write its value to the + * DataOutputStream. + * + * @param os write the value here + * @throws IOException + */ + public void writeUntagged(DataOutputStream os) + throws IOException + { + write(os); + } + + /** + * Will write the given object as a tagged value to the DataOutputStream. + * + * @param os write the value here + * @param obj the Object to write + * @throws IOException + */ + public void writeTagged(DataOutputStream os) + throws IOException + { + os.write (_tag); + write(os); + } + + /** + * This method must write the value to the DataOutputStream in a manner + * appropriate for the type of the value. + * + * @param os DataOutputStream to write to + * @throws IOException + */ + protected abstract void write(DataOutputStream os) + throws IOException; + + /** + * Returns an object representing this type + * + * @return an Object represntation of this value + */ + protected abstract Object getObject(); + + /** + * Get an untagged object from the ByteBuffer + * + * @param bb the ByteBuffer to extract the value from + * @param type a Class representing the type + * @return an Object from the ByteBuffer of the type of the Class parameter + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + public static Object getUntaggedObject(ByteBuffer bb, Class type) + throws JdwpInternalErrorException, InvalidObjectException, InvalidClassException + { + Value val = ValueFactory.createFromUntagged(bb, type); + return val.getObject(); + } + + /** + * Get an untagged object from the ByteBuffer + * + * @param bb the ByteBuffer to extract the value from + * @param tag a byte tag representing the type + * @return an Object from the ByteBuffer of the type of the Class parameter + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + public static Object getTaggedObject(ByteBuffer bb) + throws JdwpInternalErrorException, InvalidObjectException, InvalidTagException + { + Value val = ValueFactory.createFromTagged(bb); + return val.getObject(); + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/ValueFactory.java b/libjava/classpath/gnu/classpath/jdwp/value/ValueFactory.java new file mode 100644 index 00000000000..8f1a8780eeb --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/ValueFactory.java @@ -0,0 +1,247 @@ +/* ValueFactory.java -- factory to create JDWP Values + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; +import gnu.classpath.jdwp.VMIdManager; +import gnu.classpath.jdwp.exception.InvalidClassException; +import gnu.classpath.jdwp.exception.InvalidObjectException; +import gnu.classpath.jdwp.exception.InvalidTagException; +import gnu.classpath.jdwp.exception.JdwpInternalErrorException; +import gnu.classpath.jdwp.id.ObjectId; +import gnu.classpath.jdwp.util.JdwpString; + +import java.nio.ByteBuffer; + +/** + * A factory to create JDWP Values. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public class ValueFactory +{ + /** + * Creates a new Value of appropriate type for the value in the ByteBuffer + * by reading the tag byte from the front of the buffer. + * + * @param bb contains the Object + * @return A new Value of appropriate type + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + public static Value createFromTagged(ByteBuffer bb) + throws JdwpInternalErrorException, InvalidObjectException, InvalidTagException + { + return create(bb, bb.get()); + } + + /** + * Creates a new Value of appropriate type for the value in the ByteBuffer + * by checking the type of the Class passed in. + * + * @param bb contains the Object + * @param type a Class representing the type of the value in the ByteBuffer + * @return A new Value of appropriate type + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + public static Value createFromUntagged(ByteBuffer bb, Class type) + throws JdwpInternalErrorException, InvalidObjectException, InvalidClassException + { + byte tag = getTagForClass(type); + + try + { + return create(bb, tag); + } + catch (InvalidTagException ite) + { + throw new InvalidClassException(ite); + } + } + + /** + * Creates a new Value of appropriate type for the value in the ByteBuffer. + * + * @param bb contains the Object + * @param tag a byte representing the type of the object + * @return A new Value of appropriate type + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + private static Value create(ByteBuffer bb, byte tag) + throws JdwpInternalErrorException, InvalidObjectException, InvalidTagException + { + Value val = null; + switch(tag) + { + case JdwpConstants.Tag.BYTE: + val = new ByteValue(bb.get()); + break; + case JdwpConstants.Tag.BOOLEAN: + val = new BooleanValue((bb.get() != 0)); + break; + case JdwpConstants.Tag.CHAR: + val = new CharValue(bb.getChar()); + break; + case JdwpConstants.Tag.SHORT: + val = new ShortValue(bb.getShort()); + break; + case JdwpConstants.Tag.INT: + val = new IntValue(bb.getInt()); + break; + case JdwpConstants.Tag.FLOAT: + val = new FloatValue(bb.getFloat()); + break; + case JdwpConstants.Tag.LONG: + val = new LongValue(bb.getLong()); + break; + case JdwpConstants.Tag.DOUBLE: + val = new DoubleValue(bb.getDouble()); + break; + case JdwpConstants.Tag.VOID: + val = new VoidValue(); + break; + case JdwpConstants.Tag.ARRAY: + case JdwpConstants.Tag.THREAD: + case JdwpConstants.Tag.OBJECT: + case JdwpConstants.Tag.THREAD_GROUP: + case JdwpConstants.Tag.CLASS_LOADER: + case JdwpConstants.Tag.CLASS_OBJECT: + ObjectId oid = VMIdManager.getDefault().readObjectId(bb); + val = new ObjectValue(oid.getObject()); + break; + case JdwpConstants.Tag.STRING: + val = new StringValue(JdwpString.readString(bb)); + break; + default: + throw new InvalidTagException(tag); + } + + return val; + } + + /** + * Creates a tag for the type of the class. + * + * @param klass the type to get a tag for + * @return a byte tag representing the class + * @throws JdwpInternalErrorException + * @throws InvalidObjectException + */ + private static byte getTagForClass(Class klass) + throws JdwpInternalErrorException + { + byte tag; + + if (klass.isPrimitive()) + { + if (klass == byte.class) + tag = JdwpConstants.Tag.BYTE; + else if (klass == boolean.class) + tag = JdwpConstants.Tag.BOOLEAN; + else if (klass == char.class) + tag = JdwpConstants.Tag.CHAR; + else if (klass == short.class) + tag = JdwpConstants.Tag.SHORT; + else if (klass == int.class) + tag = JdwpConstants.Tag.INT; + else if (klass == float.class) + tag = JdwpConstants.Tag.FLOAT; + else if (klass == long.class) + tag = JdwpConstants.Tag.LONG; + else if (klass == double.class) + tag = JdwpConstants.Tag.DOUBLE; + else if (klass == void.class) + tag = JdwpConstants.Tag.VOID; + else + throw new JdwpInternalErrorException("Invalid primitive class"); + } + else + { + tag = JdwpConstants.Tag.OBJECT; + } + + return tag; + } + + /** + * Create a value type for an Object of type determined by a Class. This is + * a special case where a value needs to be created, but the value to create + * it for is already in an object, not in a buffer. + * + * @param value the Object to convert to a Value + * @param type the Class type of the object + * @return a new Value representing this object + */ + public static Value createFromObject(Object value, Class type) + { + Value val = null; + + if (type.isPrimitive()) + { + if (type == byte.class) + val = new ByteValue(((Byte) value).byteValue()); + else if (type == boolean.class) + val = new BooleanValue(((Boolean) value).booleanValue()); + else if (type == char.class) + val = new CharValue(((Character) value).charValue()); + else if (type == short.class) + val = new ShortValue(((Short) value).shortValue()); + else if (type == int.class) + val = new IntValue(((Integer) value).intValue()); + else if (type == float.class) + val = new FloatValue(((Float) value).floatValue()); + else if (type == long.class) + val = new LongValue(((Long) value).longValue()); + else if (type == double.class) + val = new DoubleValue(((Double) value).doubleValue()); + else if (type == void.class) + val = new VoidValue(); + } + else + { + if (type.isAssignableFrom(String.class)) + val = new StringValue ((String) value); + else + val = new ObjectValue(value); + } + + return val; + } +} diff --git a/libjava/classpath/gnu/classpath/jdwp/value/VoidValue.java b/libjava/classpath/gnu/classpath/jdwp/value/VoidValue.java new file mode 100644 index 00000000000..82cded92903 --- /dev/null +++ b/libjava/classpath/gnu/classpath/jdwp/value/VoidValue.java @@ -0,0 +1,82 @@ +/* VoidValue.java -- JDWP wrapper class for a void value + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.classpath.jdwp.value; + +import gnu.classpath.jdwp.JdwpConstants; + +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Wrapper for an void value. + * + * @author Kyle Galloway <kgallowa@redhat.com> + */ +public class VoidValue + extends Value +{ + /** + * Create a new VoidValue. + */ + public VoidValue () + { + super(JdwpConstants.Tag.VOID); + } + + /** + * Return an object representing this type + * + * @return an Object represntation of this value + */ + @Override + protected Object getObject() + { + return null; + } + + /** + * Write the wrapped void to the given DataOutputStream. + * + * @param os the output stream to write to + */ + @Override + protected void write(DataOutputStream os) + throws IOException + { + } +} diff --git a/libjava/gnu/classpath/jdwp/VMFrame.java b/libjava/gnu/classpath/jdwp/VMFrame.java index cd213025a72..de2640c85ff 100644 --- a/libjava/gnu/classpath/jdwp/VMFrame.java +++ b/libjava/gnu/classpath/jdwp/VMFrame.java @@ -1,5 +1,5 @@ /* VMFrame.java -- Reference implementation of VM hooks for JDWP Frame access. - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -40,6 +40,7 @@ exception statement from your version. */ package gnu.classpath.jdwp; import gnu.classpath.jdwp.util.Location; +import gnu.classpath.jdwp.value.Value; /** * Reference implementation of VM hooks for JDWP Frame access. @@ -54,7 +55,10 @@ public class VMFrame */ public static final int SIZE = 8; - // The object this frame resides in + // The thread this frame resides in + private Thread thread; + + //The object of this frame private Object obj; // The current location of this frame @@ -64,6 +68,22 @@ public class VMFrame private long id; /** + * Create a new VMFrame object. + * + * @param thr a Thread, the thread this frame is in + * @param frame_id a long, the jframeID of this frame + * @param frame_loc a Location, the location of this frame + */ + public VMFrame(Thread thr, long frame_id, Location frame_loc, + Object frame_obj) + { + thread = thr; + id = frame_id; + loc = frame_loc; + obj = frame_obj; + } + + /** * Gets the current location of the frame. */ public Location getLocation() @@ -76,14 +96,22 @@ public class VMFrame * * @param slot the slot containing the variable */ - public native Object getValue(int slot); + public native Value getValue(int slot, byte sig); /** * Assigns the given variable to the given value. * @param slot The slot which contains the variable * @param value The value to assign the variable to */ - public native void setValue(int slot, Object value); + public native void setValue(int slot, Value value); + + /** + * Get the thread this frame is in. + */ + public Thread getThread() + { + return thread; + } /** * Get the object which is represented by 'this' in the context of the frame, diff --git a/libjava/gnu/classpath/jdwp/VMIdManager.java b/libjava/gnu/classpath/jdwp/VMIdManager.java index 8d423e9b0d6..f787a8cdc7f 100644 --- a/libjava/gnu/classpath/jdwp/VMIdManager.java +++ b/libjava/gnu/classpath/jdwp/VMIdManager.java @@ -1,7 +1,7 @@ /* VMIdManager.java -- A reference/example implementation of a manager for JDWP object/reference type IDs - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -337,6 +337,10 @@ public class VMIdManager */ public ObjectId getObjectId (Object theObject) { + // Special case: null object. + if (theObject == null) + return new NullObjectId (); + ReferenceKey ref = new ReferenceKey (theObject, _refQueue); ObjectId id = (ObjectId) _oidTable.get (ref); if (id == null) @@ -364,6 +368,10 @@ public class VMIdManager public ObjectId get (long id) throws InvalidObjectException { + // Special case: null object id. + if (id == 0) + return new NullObjectId (); + ObjectId oid = (ObjectId) _idTable.get (new Long (id)); if (oid == null) throw new InvalidObjectException (id); diff --git a/libjava/gnu/classpath/jdwp/VMMethod.java b/libjava/gnu/classpath/jdwp/VMMethod.java index d345bc1b515..6a2b04ecf97 100644 --- a/libjava/gnu/classpath/jdwp/VMMethod.java +++ b/libjava/gnu/classpath/jdwp/VMMethod.java @@ -1,5 +1,5 @@ /* VMMethod.java -- a method in a virtual machine - Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -175,4 +175,15 @@ public class VMMethod { return VMVirtualMachine.getClassMethod(klass, bb.getLong()); } + + public boolean equals(Object obj) + { + if (obj instanceof VMMethod) + { + VMMethod m = (VMMethod) obj; + return (getId() == m.getId()); + } + + return false; + } } diff --git a/libjava/gnu/classpath/jdwp/VMVirtualMachine.java b/libjava/gnu/classpath/jdwp/VMVirtualMachine.java index 5c4018fce13..91e9965d0e2 100644 --- a/libjava/gnu/classpath/jdwp/VMVirtualMachine.java +++ b/libjava/gnu/classpath/jdwp/VMVirtualMachine.java @@ -1,7 +1,7 @@ /* VMVirtualMachine.java -- A reference implementation of a JDWP virtual machine - Copyright (C) 2005, 2006 Free Software Foundation + Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of GNU Classpath. @@ -45,11 +45,13 @@ import gnu.classpath.jdwp.event.EventRequest; import gnu.classpath.jdwp.exception.InvalidMethodException; import gnu.classpath.jdwp.exception.JdwpException; import gnu.classpath.jdwp.util.MethodResult; +import gnu.classpath.jdwp.util.MonitorInfo; + import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collection; import java.util.Hashtable; -import java.util.Iterator; /** * A virtual machine according to JDWP. @@ -58,9 +60,32 @@ import java.util.Iterator; */ public class VMVirtualMachine { + // VM Capabilities + public static final boolean canWatchFieldModification = false; + public static final boolean canWatchFieldAccess = false; + public static final boolean canGetBytecodes = false; + public static final boolean canGetSyntheticAttribute = false; + public static final boolean canGetOwnedMonitorInfo = false; + public static final boolean canGetCurrentContendedMonitor = false; + public static final boolean canGetMonitorInfo = false; + public static final boolean canRedefineClasses = false; + public static final boolean canAddMethod = false; + public static final boolean canUnrestrictedlyRedefineClasses = false; + public static final boolean canPopFrames = false; + public static final boolean canUseInstanceFilters = false; + public static final boolean canGetSourceDebugExtension = false; + public static final boolean canRequestVMDeathEvent = false; + public static final boolean canSetDefaultStratum = false; + // Thread suspension table. Maps Thread to suspend count (Integer) private static Hashtable _jdwp_suspend_counts; + // List of stepping threads: maps Thread -> stepping info + static Hashtable _stepping_threads; + + // List of co-located JVMTI events + static ArrayList _event_list; + public static native void initialize (); /** @@ -176,15 +201,9 @@ public class VMVirtualMachine throws JdwpException; /** - * Returns a count of the number of loaded classes in the VM - */ - public static native int getAllLoadedClassesCount () - throws JdwpException; - - /** - * Returns an iterator over all the loaded classes in the VM + * Returns a Collection of all classes loaded in the VM */ - public static native Iterator getAllLoadedClasses () + public static native Collection getAllLoadedClasses () throws JdwpException; /** @@ -243,7 +262,7 @@ public class VMVirtualMachine * @param bb buffer containing the frame's ID * @return the desired frame */ - public static native VMFrame getFrame (Thread thread, ByteBuffer bb) + public static native VMFrame getFrame (Thread thread, long frameID) throws JdwpException; /** @@ -332,4 +351,86 @@ public class VMVirtualMachine */ public static native void clearEvents (byte kind) throws JdwpException; + + /** + * Redefines the given types. VM must support canRedefineClasses + * capability (may also require canAddMethod and/or + * canUnrestrictedlyRedefineClasses capabilities) + * + * @param types the classes to redefine + * @param bytecodes the new bytecode definitions for the classes + */ + public static native void redefineClasses(Class[] types, byte[][] bytecodes) + throws JdwpException; + + /** + * Sets the default stratum. VM must support the + * canSetDefaultStratum capability. + * + * @param stratum the new default stratum or empty string to + * use the reference default + */ + public static native void setDefaultStratum(String stratum) + throws JdwpException; + + /** + * Returns the source debug extension. VM must support the + * canGetSourceDebugExtension capability. + * + * @param klass the class for which to return information + * @returns the source debug extension + */ + public static native String getSourceDebugExtension(Class klass) + throws JdwpException; + + /** + * Returns the bytecode for the given method. VM must support the + * canGetBytecodes capability. + * + * @param method the method for which to get bytecodes + * @returns the bytecodes + */ + public static native byte[] getBytecodes(VMMethod method) + throws JdwpException; + + /** + * Returns monitor information about an object. VM must support + * the canGetMonitorInformation capability. + * + * @param obj the object + * @returns monitor information (owner, entry count, waiters) + */ + public static native MonitorInfo getMonitorInfo(Object obj) + throws JdwpException; + + /** + * Returns a list of owned monitors. VM must support the + * canGetOwnedMonitorInfo capability. + * + * @param thread a thread + * @returns the list of monitors owned by this thread + */ + public static native Object[] getOwnedMonitors(Thread thread) + throws JdwpException; + + /** + * Returns the current contended monitor for a thread. VM must + * support canGetCurrentContendedMonitor capability. + * + * @param thread the thread + * @returns the contended monitor + */ + public static native Object getCurrentContendedMonitor(Thread thread) + throws JdwpException; + + /** + * Pop all frames up to and including the given frame. VM must + * support canPopFrames capability. It is the responsibility + * of the VM to check if the thread is suspended. If it is not, + * the VM should throw ThreadNotSuspendedException. + * + * @param thread the thread + * @param frame the frame ID + */ + public static native void popFrames(Thread thread, long frameId); } diff --git a/libjava/gnu/classpath/jdwp/natVMFrame.cc b/libjava/gnu/classpath/jdwp/natVMFrame.cc index de3b844f4a4..6f2d5233233 100644 --- a/libjava/gnu/classpath/jdwp/natVMFrame.cc +++ b/libjava/gnu/classpath/jdwp/natVMFrame.cc @@ -1,6 +1,6 @@ // natFrame.cc -- native support for VMFrame.java -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -8,19 +8,331 @@ This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ +#include <config.h> #include <gcj/cni.h> +#include <jvm.h> +#include <jvmti.h> +#include "jvmti-int.h" + +#include <java-interp.h> #include <gnu/classpath/jdwp/VMFrame.h> +#include <gnu/classpath/jdwp/VMVirtualMachine.h> +#include <gnu/classpath/jdwp/exception/InvalidFrameException.h> +#include <gnu/classpath/jdwp/exception/InvalidSlotException.h> +#include <gnu/classpath/jdwp/exception/InvalidThreadException.h> +#include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h> +#include <gnu/classpath/jdwp/exception/TypeMismatchException.h> +#include <gnu/classpath/jdwp/util/NullObject.h> +#include <gnu/classpath/jdwp/value/ArrayValue.h> +#include <gnu/classpath/jdwp/value/ByteValue.h> +#include <gnu/classpath/jdwp/value/BooleanValue.h> +#include <gnu/classpath/jdwp/value/CharValue.h> +#include <gnu/classpath/jdwp/value/DoubleValue.h> +#include <gnu/classpath/jdwp/value/FloatValue.h> +#include <gnu/classpath/jdwp/value/IntValue.h> +#include <gnu/classpath/jdwp/value/LongValue.h> +#include <gnu/classpath/jdwp/value/ObjectValue.h> +#include <gnu/classpath/jdwp/value/ShortValue.h> +#include <gnu/classpath/jdwp/value/Value.h> +#include <gnu/classpath/jdwp/value/VoidValue.h> using namespace java::lang; +using namespace gnu::classpath::jdwp; +using namespace gnu::classpath::jdwp::exception; + -Object* -gnu::classpath::jdwp::VMFrame::getValue (jint slot) +// All the jvmti GetLocalXX and SetLocalXX functions return the same potential +// errors, so this function handles them all and throws the appropriate JDWP +// exception. +static void +checkJVMTIError (jvmtiEnv *env, jthread thread, jvmtiError jerr, jint slot, + jbyte sig) { - return 0; + if (jerr != JVMTI_ERROR_NONE) + { + char *error; + env->GetErrorName (jerr, &error); + String *msg = reinterpret_cast<String *> (JvNewStringUTF (error)); + env->Deallocate ((unsigned char *) error); + + if (jerr == JVMTI_ERROR_INVALID_THREAD) + throw new InvalidThreadException ((jlong) thread); + else if (jerr == JVMTI_ERROR_NO_MORE_FRAMES) + throw new InvalidFrameException (msg); + else if (jerr == JVMTI_ERROR_INVALID_SLOT) + throw new InvalidSlotException (slot); + else if (jerr == JVMTI_ERROR_TYPE_MISMATCH) + throw new TypeMismatchException (sig); + else + throw new JdwpInternalErrorException (msg); + } } -void -gnu::classpath::jdwp::VMFrame::setValue (jint slot, Object* value) + +static jobject +getObjectJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig) +{ + jobject value; + jvmtiError jerr = env->GetLocalObject (thread, depth, slot, &value); + + checkJVMTIError (env, thread, jerr, slot, sig); + + return value; +} + +static void +setObjectJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, + jbyte sig, jobject value) +{ + if (value->getClass ()->isAssignableFrom (&util::NullObject::class$)) + value = NULL; + + jvmtiError jerr = env->SetLocalObject (thread, depth, slot, value); + + checkJVMTIError (env, thread, jerr, slot, sig); +} + +static jint +getIntJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig) +{ + jint value; + jvmtiError jerr = env->GetLocalInt (thread, depth, slot, &value); + + checkJVMTIError (env, thread, jerr, slot, sig); + return value; +} + +static void +setIntJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig, + jint value) +{ + jvmtiError jerr = env->SetLocalInt (thread, depth, slot, value); + + checkJVMTIError (env, thread, jerr, slot, sig); +} + +static jlong +getLongJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig) +{ + jlong value; + jvmtiError jerr = env->GetLocalLong (thread, depth, slot, &value); + + checkJVMTIError (env, thread, jerr, slot, sig); + + return value; +} + +static void +setLongJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig, + jlong value) +{ + jvmtiError jerr = env->SetLocalLong (thread, depth, slot, value); + + checkJVMTIError (env, thread, jerr, slot, sig); +} + +static jfloat +getFloatJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig) +{ + jfloat value; + jvmtiError jerr = env->GetLocalFloat (thread, depth, slot, &value); + + checkJVMTIError (env, thread, jerr, slot, sig); + + return value; +} + +static void +setFloatJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, jbyte sig, + jfloat value) { + jvmtiError jerr = env->SetLocalFloat (thread, depth, slot, value); + + checkJVMTIError (env, thread, jerr, slot, sig); +} + +static jdouble +getDoubleJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, + jbyte sig) +{ + jdouble value; + jvmtiError jerr = env->GetLocalDouble (thread, depth, slot, &value); + + checkJVMTIError (env, thread, jerr, slot, sig); + + return value; +} + +static void +setDoubleJVMTI (jvmtiEnv *env, jthread thread, jint slot, jint depth, + jbyte sig, jdouble value) +{ + jvmtiError jerr = env->SetLocalDouble (thread, depth, slot, value); + + checkJVMTIError (env, thread, jerr, slot, sig); +} + +// This is necessary since JVMTI requires a stack depth as a parameter in all +// its local variable functions. Since JDWP needs frameids, we have to run +// through the call stack to translate these ids into the parameters JVMTI +// wants. +static jint +getFrameDepth (_Jv_Frame *frame) +{ + jint depth = 0; + _Jv_Frame *top_frame = (_Jv_Frame *) frame->thread->frame; + jint num_frames = VMVirtualMachine::getFrameCount (frame->thread); + + while (frame != top_frame) + { + top_frame = top_frame->next; + depth++; + + if (depth >= num_frames || top_frame == NULL) + throw new InvalidFrameException ((jlong) frame); + } + + return depth; +} + +using namespace gnu::classpath::jdwp::value; + +Value * +gnu::classpath::jdwp::VMFrame::getValue (jint slot, jbyte sig) +{ + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (id); + jint depth = getFrameDepth (frame); + jthread thread = reinterpret_cast<jthread> (frame->thread); + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + + Value *value = NULL; + + switch (sig) + { + case 'B': + value = new ByteValue ((jbyte) getIntJVMTI (env, thread, slot, depth, + sig)); + break; + case 'Z': + value = new BooleanValue ((jboolean) getIntJVMTI (env, thread, slot, + depth, sig)); + break; + case 'C': + value = new CharValue ((jchar) getIntJVMTI (env, thread, slot, depth, + sig)); + break; + case 'S': + value = new ShortValue ((jshort) getIntJVMTI (env, thread, slot, depth, + sig)); + break; + case 'I': + value = new IntValue (getIntJVMTI (env, thread, slot, depth, sig)); + break; + case 'J': + value = new LongValue (getLongJVMTI (env, thread, slot, depth, sig)); + break; + case 'F': + value = new FloatValue (getFloatJVMTI (env, thread, slot, depth, sig)); + break; + case 'D': + value = new DoubleValue (getDoubleJVMTI (env, thread, slot, depth, sig)); + break; + case 'V': + value = new VoidValue (); + break; + case '[': + { + Object *obj = getObjectJVMTI (env, thread, slot, depth, sig); + if (obj == NULL) + obj = new util::NullObject (); + value = new ArrayValue (obj); + break; + } + default: + Object *obj = getObjectJVMTI (env, thread, slot, depth, sig); + if (obj == NULL) + obj = new util::NullObject (); + value = new ObjectValue (obj); + break; + } + + return value; +} + +void +gnu::classpath::jdwp::VMFrame::setValue (jint slot, Value* value) +{ + jbyte sig = value->getTag (); + + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (id); + jint depth = getFrameDepth (frame); + jthread thread = reinterpret_cast<jthread> (frame->thread); + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + + switch (sig) + { + case 'B': + { + ByteValue *val = reinterpret_cast<ByteValue *> (value); + setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ()); + break; + } + case 'Z': + { + BooleanValue *val = reinterpret_cast<BooleanValue *> (value); + setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ()); + break; + } + case 'C': + { + CharValue *val = reinterpret_cast<CharValue *> (value); + setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ()); + break; + } + case 'S': + { + ShortValue *val = reinterpret_cast<ShortValue *> (value); + setIntJVMTI (env, thread, slot, depth, sig, (jint) val->getValue ()); + break; + } + case 'I': + { + IntValue *val = reinterpret_cast<IntValue *> (value); + setIntJVMTI (env, thread, slot, depth, sig, val->getValue ()); + break; + } + case 'J': + { + LongValue *val = reinterpret_cast<LongValue *> (value); + setLongJVMTI (env, thread, slot, depth, sig, val->getValue ()); + break; + } + case 'F': + { + FloatValue *val = reinterpret_cast<FloatValue *> (value); + setFloatJVMTI (env, thread, slot, depth, sig, val->getValue ()); + break; + } + case 'D': + { + DoubleValue *val = reinterpret_cast<DoubleValue *> (value); + setDoubleJVMTI (env, thread, slot, depth, sig, val->getValue ()); + break; + } + case 'V': + break; + case '[': + { + ArrayValue *val = reinterpret_cast<ArrayValue *> (value); + setObjectJVMTI (env, thread, slot, depth, sig, val->getObject ()); + break; + } + default: + { + ObjectValue *val = reinterpret_cast<ObjectValue *> (value); + setObjectJVMTI (env, thread, slot, depth, sig, val->getObject()); + break; + } + } } diff --git a/libjava/gnu/classpath/jdwp/natVMMethod.cc b/libjava/gnu/classpath/jdwp/natVMMethod.cc index 7dea4747731..1cea54dae9d 100644 --- a/libjava/gnu/classpath/jdwp/natVMMethod.cc +++ b/libjava/gnu/classpath/jdwp/natVMMethod.cc @@ -1,6 +1,6 @@ // natVMMethod.cc -- native support for VMMethod -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -11,39 +11,74 @@ details. */ #include <config.h> #include <gcj/cni.h> #include <java-interp.h> +#include <jvmti.h> +#include "jvmti-int.h" +#include <java/lang/reflect/Modifier.h> #include <gnu/classpath/jdwp/VMMethod.h> +#include <gnu/classpath/jdwp/exception/AbsentInformationException.h> +#include <gnu/classpath/jdwp/exception/InvalidMethodException.h> #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h> #include <gnu/classpath/jdwp/util/LineTable.h> #include <gnu/classpath/jdwp/util/VariableTable.h> -java::lang::String* +using namespace java::lang; + +#define CHECK_INTERP_CLASS() \ +do \ + { \ + if (!_Jv_IsInterpretedClass (getDeclaringClass ())) \ + { \ + ::java::lang::String *msg = JvNewStringLatin1 ("native class"); \ + throw new exception::JdwpInternalErrorException (msg); \ + } \ + } \ +while (0) + +jstring gnu::classpath::jdwp::VMMethod::getName () { - return NULL; + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + jmethodID method = reinterpret_cast<jmethodID> (_methodId); + char *name; + env->GetMethodName (method, &name, NULL, NULL); + jstring string = JvNewStringUTF (name); + env->Deallocate (reinterpret_cast<unsigned char *> (name)); + return string; } -java::lang::String* +jstring gnu::classpath::jdwp::VMMethod::getSignature () { - return NULL; + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + jmethodID method = reinterpret_cast<jmethodID> (_methodId); + char *signature; + env->GetMethodName (method, NULL, &signature, NULL); + jstring string = JvNewStringUTF (signature); + env->Deallocate (reinterpret_cast<unsigned char *> (signature)); + return string; } jint gnu::classpath::jdwp::VMMethod::getModifiers () { - return 0; + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + jmethodID method = reinterpret_cast<jmethodID> (_methodId); + jint flags; + env->GetMethodModifiers (method, &flags); + + // If this class is compiled, as far as JDWP is concerned, its methods are + // native. This will set the native flag for these methods. + if (!_Jv_IsInterpretedClass (getDeclaringClass ())) + flags |= ::java::lang::reflect::Modifier::NATIVE; + + return flags; } gnu::classpath::jdwp::util::LineTable * gnu::classpath::jdwp::VMMethod::getLineTable () { - if (!_Jv_IsInterpretedClass (getDeclaringClass ())) - { - // this should not happen - ::java::lang::String *msg = JvNewStringLatin1 ("native class"); - throw new exception::JdwpInternalErrorException (msg); - } + CHECK_INTERP_CLASS (); jmethodID desired_method = reinterpret_cast<jmethodID> (_methodId); @@ -79,5 +114,78 @@ gnu::classpath::jdwp::VMMethod::getLineTable () gnu::classpath::jdwp::util::VariableTable* gnu::classpath::jdwp::VMMethod::getVariableTable () { - return NULL; + using namespace gnu::classpath::jdwp::util; + + jvmtiEnv *env = _Jv_GetJDWP_JVMTIEnv (); + + CHECK_INTERP_CLASS (); + + jmethodID meth = reinterpret_cast<jmethodID> (_methodId); + jvmtiLocalVariableEntry *var_table; + jint num_slots, args_len; + + jvmtiError jerr = env->GetLocalVariableTable (meth, &num_slots, &var_table); + + if (jerr != JVMTI_ERROR_NONE) + goto error; + + jerr = env->GetArgumentsSize (meth, &args_len); + + if (jerr != JVMTI_ERROR_NONE) + { + error: + using namespace gnu::classpath::jdwp::exception; + char *error; + env->GetErrorName (jerr, &error); + String *msg = JvNewStringUTF (error); + env->Deallocate (reinterpret_cast<unsigned char *> (error)); + + if (jerr == JVMTI_ERROR_NATIVE_METHOD) + throw new AbsentInformationException (msg); + else if (jerr == JVMTI_ERROR_INVALID_METHODID) + throw new InvalidMethodException (_methodId); + else + throw new JdwpInternalErrorException (msg); + } + + jlongArray start_pcs = JvNewLongArray (num_slots); + jlong *start_pcs_ptr = elements (start_pcs); + jintArray lengths = JvNewIntArray (num_slots); + jint *lengths_ptr = elements (lengths); + jintArray slots = JvNewIntArray (num_slots); + jint *slots_ptr = elements (slots); + JArray<String *> *names = reinterpret_cast<JArray<String *> *> + (JvNewObjectArray (num_slots, + &String::class$, NULL)); + jstring *names_ptr = elements (names); + JArray<String *> *signatures = reinterpret_cast<JArray<String *> *> + (JvNewObjectArray (num_slots, + &String::class$, NULL)); + jstring *signatures_ptr = elements (signatures); + + // Get the information out of the JVMTI strucutre and Deallocate the strings. + for (int i = 0; i < num_slots; i++) + { + start_pcs_ptr[i] = var_table[i].start_location; + lengths_ptr[i] = var_table[i].length; + slots_ptr[i] = var_table[i].slot; + names_ptr[i] = JvNewStringUTF (var_table[i].name); + env->Deallocate (reinterpret_cast<unsigned char *> + (var_table[i].name)); + signatures_ptr[i] = JvNewStringUTF (var_table[i].signature); + env->Deallocate (reinterpret_cast<unsigned char *> + (var_table[i].signature)); + env->Deallocate (reinterpret_cast<unsigned char *> + (var_table[i].generic_signature)); + } + + // Now Deallocate the table since it's strings have already been freed. + env->Deallocate (reinterpret_cast<unsigned char *> (var_table)); + + // Create the new JDWP VariableTable to return with the now filled arrays. + VariableTable* jdwp_vtable = new VariableTable (args_len, num_slots, + start_pcs, names, signatures, + lengths, slots); + + return jdwp_vtable; } diff --git a/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc index 351bb23ccb2..db33781378e 100644 --- a/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc +++ b/libjava/gnu/classpath/jdwp/natVMVirtualMachine.cc @@ -10,6 +10,8 @@ details. */ #include <config.h> #include <gcj/cni.h> +#include <java-assert.h> +#include <java-interp.h> #include <jvm.h> #include <jvmti.h> @@ -19,40 +21,111 @@ details. */ #include <java/lang/String.h> #include <java/lang/StringBuilder.h> #include <java/lang/Thread.h> +#include <java/lang/Throwable.h> #include <java/nio/ByteBuffer.h> +#include <java/nio/ByteBufferImpl.h> #include <java/util/ArrayList.h> +#include <java/util/Collection.h> #include <java/util/Hashtable.h> #include <java/util/Iterator.h> #include <gnu/classpath/jdwp/Jdwp.h> +#include <gnu/classpath/jdwp/JdwpConstants$StepDepth.h> +#include <gnu/classpath/jdwp/JdwpConstants$StepSize.h> +#include <gnu/classpath/jdwp/JdwpConstants$ThreadStatus.h> #include <gnu/classpath/jdwp/VMFrame.h> #include <gnu/classpath/jdwp/VMMethod.h> #include <gnu/classpath/jdwp/VMVirtualMachine.h> +#include <gnu/classpath/jdwp/event/BreakpointEvent.h> +#include <gnu/classpath/jdwp/event/ClassPrepareEvent.h> +#include <gnu/classpath/jdwp/event/ExceptionEvent.h> +#include <gnu/classpath/jdwp/event/EventManager.h> #include <gnu/classpath/jdwp/event/EventRequest.h> +#include <gnu/classpath/jdwp/event/SingleStepEvent.h> +#include <gnu/classpath/jdwp/event/ThreadEndEvent.h> +#include <gnu/classpath/jdwp/event/ThreadStartEvent.h> +#include <gnu/classpath/jdwp/event/VmDeathEvent.h> #include <gnu/classpath/jdwp/event/VmInitEvent.h> +#include <gnu/classpath/jdwp/event/filters/IEventFilter.h> +#include <gnu/classpath/jdwp/event/filters/LocationOnlyFilter.h> +#include <gnu/classpath/jdwp/event/filters/StepFilter.h> +#include <gnu/classpath/jdwp/exception/AbsentInformationException.h> +#include <gnu/classpath/jdwp/exception/InvalidFrameException.h> +#include <gnu/classpath/jdwp/exception/InvalidLocationException.h> +#include <gnu/classpath/jdwp/exception/InvalidMethodException.h> #include <gnu/classpath/jdwp/exception/JdwpInternalErrorException.h> +#include <gnu/classpath/jdwp/id/ThreadId.h> +#include <gnu/classpath/jdwp/util/Location.h> #include <gnu/classpath/jdwp/util/MethodResult.h> +#include <gnu/gcj/jvmti/Breakpoint.h> +#include <gnu/gcj/jvmti/BreakpointManager.h> using namespace java::lang; using namespace gnu::classpath::jdwp::event; using namespace gnu::classpath::jdwp::util; +// Stepping information +struct step_info +{ + jint size; // See gnu.classpath.jdwp.JdwpConstants.StepSize + jint depth; // See gnu.classpath.jdwp.JdwpConstants.StepDepth + int stack_depth; // stack depth at start of stepping + jmethodID method; // method in which we are stepping +}; + // Forward declarations -static void jdwpVMInitCB (jvmtiEnv *env, JNIEnv *jni_env, jthread thread); +static jvmtiError get_linetable (jvmtiEnv *, jmethodID, jint *, + jvmtiLineNumberEntry **); +static Location *get_request_location (EventRequest *); +static gnu::classpath::jdwp::event::filters::StepFilter * +get_request_step_filter (EventRequest *); +static void handle_single_step (jvmtiEnv *, struct step_info *, jthread, + jmethodID, jlocation); +static void JNICALL jdwpBreakpointCB (jvmtiEnv *, JNIEnv *, jthread, + jmethodID, jlocation); +static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass); +static void JNICALL jdwpExceptionCB (jvmtiEnv *, JNIEnv *jni_env, jthread, + jmethodID, jlocation, jobject, + jmethodID, jlocation); +static void JNICALL jdwpSingleStepCB (jvmtiEnv *, JNIEnv *, jthread, + jmethodID, jlocation); +static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread); +static void JNICALL jdwpThreadStartCB (jvmtiEnv *, JNIEnv *, jthread); +static void JNICALL jdwpVMDeathCB (jvmtiEnv *, JNIEnv *); +static void JNICALL jdwpVMInitCB (jvmtiEnv *, JNIEnv *, jthread); +static void throw_jvmti_error (jvmtiError); #define DEFINE_CALLBACK(Cb,Event) Cb.Event = jdwp ## Event ## CB +#define DISABLE_EVENT(Event,Thread) \ + _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_DISABLE, \ + JVMTI_EVENT_ ## Event, Thread) #define ENABLE_EVENT(Event,Thread) \ _jdwp_jvmtiEnv->SetEventNotificationMode (JVMTI_ENABLE, \ JVMTI_EVENT_ ## Event, Thread) // JVMTI environment static jvmtiEnv *_jdwp_jvmtiEnv; +jvmtiEnv * +_Jv_GetJDWP_JVMTIEnv (void) +{ + return _jdwp_jvmtiEnv; +} + void gnu::classpath::jdwp::VMVirtualMachine::initialize () { _jdwp_suspend_counts = new ::java::util::Hashtable (); + _stepping_threads = new ::java::util::Hashtable (); + _event_list = new ::java::util::ArrayList (); + JavaVM *vm = _Jv_GetJavaVM (); - vm->GetEnv (reinterpret_cast<void **> (&_jdwp_jvmtiEnv), JVMTI_VERSION_1_0); + union + { + void *ptr; + jvmtiEnv *env; + } foo; + vm->GetEnv (&(foo.ptr), JVMTI_VERSION_1_0); + _jdwp_jvmtiEnv = foo.env; // Wait for VM_INIT to do more initialization jvmtiEventCallbacks callbacks; @@ -62,7 +135,7 @@ gnu::classpath::jdwp::VMVirtualMachine::initialize () } void -gnu::classpath::jdwp::VMVirtualMachine ::suspendThread (Thread *thread) +gnu::classpath::jdwp::VMVirtualMachine::suspendThread (Thread *thread) { jint value; Integer *count; @@ -170,9 +243,57 @@ gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request) switch (request->getEventKind ()) { case EventRequest::EVENT_SINGLE_STEP: + { + Thread *thread; + filters::StepFilter *filter = get_request_step_filter (request); + if (filter == NULL) + { + // No filter specified: report every step in every + // thread. + thread = NULL; + } + else + { + // Add stepping information to list of stepping threads + thread = filter->getThread ()->getThread (); + _Jv_InterpFrame *frame + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + struct step_info *sinfo + = (struct step_info *) JvAllocBytes (sizeof (struct step_info)); + sinfo->size = filter->getSize (); + sinfo->depth = filter->getDepth (); + sinfo->stack_depth = frame->depth (); + sinfo->method = frame->self->get_method (); + _stepping_threads->put (thread, (jobject) sinfo); + } + + ENABLE_EVENT (SINGLE_STEP, thread); + } break; case EventRequest::EVENT_BREAKPOINT: + { + using namespace ::gnu::gcj::jvmti; + Location *loc = get_request_location (request); + if (loc == NULL) + { + using namespace gnu::classpath::jdwp::exception; + throw new InvalidLocationException (); + } + + jlong method = loc->getMethod ()->getId (); + jlocation index = loc->getIndex (); + Breakpoint *bp = BreakpointManager::getBreakpoint (method, index); + if (bp == NULL) + { + // Breakpoint not in interpreter yet + bp = BreakpointManager::newBreakpoint (method, index); + } + else + { + // Ignore the duplicate + } + } break; case EventRequest::EVENT_FRAME_POP: @@ -213,7 +334,7 @@ gnu::classpath::jdwp::VMVirtualMachine::registerEvent (EventRequest *request) case EventRequest::EVENT_VM_INIT: break; - + case EventRequest::EVENT_VM_DEATH: break; } @@ -225,9 +346,62 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request) switch (request->getEventKind ()) { case EventRequest::EVENT_SINGLE_STEP: + { + Thread *thread; + filters::StepFilter *filter = get_request_step_filter (request); + if (filter == NULL) + thread = NULL; + else + { + thread = filter->getThread ()->getThread (); + _stepping_threads->remove (thread); + } + + DISABLE_EVENT (SINGLE_STEP, thread); + } break; case EventRequest::EVENT_BREAKPOINT: + { + using namespace gnu::gcj::jvmti; + ::java::util::Collection *breakpoints; + EventManager *em = EventManager::getDefault (); + breakpoints = em->getRequests (EventRequest::EVENT_BREAKPOINT); + + // Check for duplicates + int matches = 0; + Location *the_location = get_request_location (request); + + // This should not be possible: we REQUIRE a Location + // to install a breakpoint + JvAssert (the_location != NULL); + + ::java::util::Iterator *iter = breakpoints->iterator (); + while (iter->hasNext ()) + { + EventRequest *er + = reinterpret_cast<EventRequest *> (iter->next ()); + Location *loc = get_request_location (er); + JvAssert (loc != NULL); + if (loc->equals (the_location) && ++matches == 2) + { + // Short-circuit: already more than one breakpoint + return; + } + } + + if (matches == 0) + { + using namespace gnu::classpath::jdwp::exception; + jstring msg + = JvNewStringLatin1 ("attempt to remove unknown breakpoint"); + throw new JdwpInternalErrorException (msg); + } + + jlong methodId = the_location->getMethod ()->getId (); + BreakpointManager::deleteBreakpoint (methodId, + the_location->getIndex ()); + } break; case EventRequest::EVENT_FRAME_POP: @@ -244,7 +418,7 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request) case EventRequest::EVENT_THREAD_END: break; - + case EventRequest::EVENT_CLASS_PREPARE: break; @@ -268,103 +442,718 @@ gnu::classpath::jdwp::VMVirtualMachine::unregisterEvent (EventRequest *request) case EventRequest::EVENT_VM_INIT: break; - + case EventRequest::EVENT_VM_DEATH: break; } } void -gnu::classpath::jdwp::VMVirtualMachine::clearEvents (jbyte kind) +gnu::classpath::jdwp::VMVirtualMachine::clearEvents (MAYBE_UNUSED jbyte kind) { } -jint -gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClassesCount (void) -{ - return 0; -} - -java::util::Iterator * +java::util::Collection * gnu::classpath::jdwp::VMVirtualMachine::getAllLoadedClasses (void) { - return NULL; + using namespace ::java::util; + return (Collection *) new ArrayList (); } jint -gnu::classpath::jdwp::VMVirtualMachine::getClassStatus (jclass klass) +gnu::classpath::jdwp::VMVirtualMachine:: +getClassStatus (jclass klass) { - return 0; + jint flags = 0; + jvmtiError err = _jdwp_jvmtiEnv->GetClassStatus (klass, &flags); + if (err != JVMTI_ERROR_NONE) + throw_jvmti_error (err); + + using namespace gnu::classpath::jdwp::event; + jint status = 0; + if (flags & JVMTI_CLASS_STATUS_VERIFIED) + status |= ClassPrepareEvent::STATUS_VERIFIED; + if (flags & JVMTI_CLASS_STATUS_PREPARED) + status |= ClassPrepareEvent::STATUS_PREPARED; + if (flags & JVMTI_CLASS_STATUS_ERROR) + status |= ClassPrepareEvent::STATUS_ERROR; + if (flags & JVMTI_CLASS_STATUS_INITIALIZED) + status |= ClassPrepareEvent::STATUS_INITIALIZED; + + return status; } JArray<gnu::classpath::jdwp::VMMethod *> * -gnu::classpath::jdwp::VMVirtualMachine::getAllClassMethods (jclass klass) +gnu::classpath::jdwp::VMVirtualMachine:: +getAllClassMethods (jclass klass) { - return NULL; + jint count; + jmethodID *methods; + jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods); + if (err != JVMTI_ERROR_NONE) + throw_jvmti_error (err); + + JArray<VMMethod *> *result + = (JArray<VMMethod *> *) JvNewObjectArray (count, + &VMMethod::class$, NULL); + VMMethod **rmeth = elements (result); + for (int i = 0; i < count; ++i) + { + jlong id = reinterpret_cast<jlong> (methods[i]); + rmeth[i] = getClassMethod (klass, id); + } + + _jdwp_jvmtiEnv->Deallocate ((unsigned char *) methods); + return result; } gnu::classpath::jdwp::VMMethod * -gnu::classpath::jdwp::VMVirtualMachine::getClassMethod (jclass klass, jlong id) +gnu::classpath::jdwp::VMVirtualMachine:: +getClassMethod (jclass klass, jlong id) { - return NULL; + jint count; + jmethodID *methods; + jvmtiError err = _jdwp_jvmtiEnv->GetClassMethods (klass, &count, &methods); + if (err != JVMTI_ERROR_NONE) + throw_jvmti_error (err); + + jmethodID meth_id = reinterpret_cast<jmethodID> (id); + + using namespace gnu::classpath::jdwp; + + // Check if this method is defined for the given class and if so return a + // VMMethod representing it. + for (int i = 0; i < count; i++) + { + if (methods[i] == meth_id) + return new VMMethod (klass, reinterpret_cast<jlong> (meth_id)); + } + + throw new exception::InvalidMethodException (id); } java::util::ArrayList * -gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, - jint start, - jint length) +gnu::classpath::jdwp::VMVirtualMachine::getFrames (Thread *thread, jint start, + jint length) { - return NULL; + jint frame_count = getFrameCount (thread); + ::java::util::ArrayList *frame_list; + + // Calculate the max number of frames to be returned. + jint num_frames = frame_count - start; + + // Check if num_frames is valid. + if (num_frames < 0) + num_frames = 0; + + // Check if there are more than length frames left after start. + // If length ios -1 return all remaining frames. + if (length != -1 && num_frames > length) + num_frames = length; + + frame_list = new ::java::util::ArrayList (num_frames); + + _Jv_Frame *vm_frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + + // Take start frames off the top of the stack + while (vm_frame != NULL && start > 0) + { + start--; + vm_frame = vm_frame->next; + } + + // Use as a counter for the number of frames returned. + num_frames = 0; + + while (vm_frame != NULL && (num_frames < length || length == -1)) + { + jlong frameId = reinterpret_cast<jlong> (vm_frame); + + VMFrame *frame = getFrame (thread, frameId); + frame_list->add (frame); + vm_frame = vm_frame->next; + num_frames++; + } + + return frame_list; } gnu::classpath::jdwp::VMFrame * -gnu::classpath::jdwp::VMVirtualMachine::getFrame (Thread *thread, - ::java::nio::ByteBuffer *bb) +gnu::classpath::jdwp::VMVirtualMachine:: +getFrame (Thread *thread, jlong frameID) { - return NULL; + using namespace gnu::classpath::jdwp::exception; + + _Jv_Frame *vm_frame = (_Jv_Frame *) thread->frame; + jint depth = 0; + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (frameID); + + // We need to find the stack depth of the frame, so search through the call + // stack to find it. This also checks for a valid frameID. + while (vm_frame != frame) + { + vm_frame = vm_frame->next; + depth++; + if (vm_frame == NULL) + throw new InvalidFrameException (frameID); + } + + Location *loc = NULL; + jvmtiFrameInfo info; + jvmtiError jerr; + jint num_frames; + jclass klass; + + // Get the info for the frame of interest + jerr = _jdwp_jvmtiEnv->GetStackTrace (thread, depth, 1, &info, &num_frames); + + if (jerr != JVMTI_ERROR_NONE) + throw_jvmti_error (jerr); + + jerr = _jdwp_jvmtiEnv->GetMethodDeclaringClass (info.method, &klass); + + if (jerr != JVMTI_ERROR_NONE) + throw_jvmti_error (jerr); + + VMMethod *meth + = getClassMethod (klass, reinterpret_cast<jlong> (info.method)); + + jobject this_obj; + + if (info.location == -1) + { + loc = new Location (meth, 0); + this_obj = NULL; + } + else + { + loc = new Location (meth, info.location); + _Jv_InterpFrame *iframe = reinterpret_cast<_Jv_InterpFrame *> (vm_frame); + this_obj = iframe->get_this_ptr (); + } + + return new VMFrame (thread, reinterpret_cast<jlong> (vm_frame), loc, + this_obj); } jint -gnu::classpath::jdwp::VMVirtualMachine::getFrameCount (Thread *thread) +gnu::classpath::jdwp::VMVirtualMachine:: +getFrameCount (Thread *thread) { - return 0; + jint frame_count; + + jvmtiError jerr = _jdwp_jvmtiEnv->GetFrameCount (thread, &frame_count); + + if (jerr != JVMTI_ERROR_NONE) + throw_jvmti_error (jerr); + + return frame_count; } jint -gnu::classpath::jdwp::VMVirtualMachine::getThreadStatus (Thread *thread) +gnu::classpath::jdwp::VMVirtualMachine:: +getThreadStatus (Thread *thread) { - return 0; + jint thr_state, status; + + jvmtiError jerr = _jdwp_jvmtiEnv->GetThreadState (thread, &thr_state); + if (jerr != JVMTI_ERROR_NONE) + throw_jvmti_error (jerr); + + if (thr_state & JVMTI_THREAD_STATE_SLEEPING) + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::SLEEPING; + else if (thr_state & JVMTI_THREAD_STATE_RUNNABLE) + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING; + else if (thr_state & JVMTI_THREAD_STATE_WAITING) + { + if (thr_state & (JVMTI_THREAD_STATE_IN_OBJECT_WAIT + | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER)) + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::MONITOR; + else + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::WAIT; + } + else + { + // The thread is not SLEEPING, MONITOR, or WAIT. It may, however, be + // alive but not yet started. + if (!(thr_state & (JVMTI_THREAD_STATE_ALIVE + | JVMTI_THREAD_STATE_TERMINATED))) + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::RUNNING; + status = gnu::classpath::jdwp::JdwpConstants$ThreadStatus::ZOMBIE; + } + + return status; } java::util::ArrayList * -gnu::classpath::jdwp::VMVirtualMachine::getLoadRequests (ClassLoader *cl) +gnu::classpath::jdwp::VMVirtualMachine:: +getLoadRequests (MAYBE_UNUSED ClassLoader *cl) { - return NULL; + return new ::java::util::ArrayList (); } MethodResult * -gnu::classpath::jdwp::VMVirtualMachine::executeMethod (jobject obj, - Thread *thread, - jclass clazz, - reflect::Method *method, - jobjectArray values, - jboolean nonVirtual) +gnu::classpath::jdwp::VMVirtualMachine:: +executeMethod (MAYBE_UNUSED jobject obj, MAYBE_UNUSED Thread *thread, + MAYBE_UNUSED jclass clazz, MAYBE_UNUSED reflect::Method *method, + MAYBE_UNUSED jobjectArray values, + MAYBE_UNUSED jboolean nonVirtual) { return NULL; } jstring -gnu::classpath::jdwp::VMVirtualMachine::getSourceFile (jclass clazz) +gnu::classpath::jdwp::VMVirtualMachine:: +getSourceFile (jclass clazz) +{ + jstring file = _Jv_GetInterpClassSourceFile (clazz); + + // Check if the source file was found. + if (file == NULL) + throw new exception::AbsentInformationException ( + _Jv_NewStringUTF("Source file not found")); + + return file; +} + +void +gnu::classpath::jdwp::VMVirtualMachine:: +redefineClasses (MAYBE_UNUSED JArray<jclass> *types, + MAYBE_UNUSED JArray<jbyteArray> *bytecodes) +{ +} + +void +gnu::classpath::jdwp::VMVirtualMachine:: +setDefaultStratum (MAYBE_UNUSED jstring stratum) +{ +} + +jstring +gnu::classpath::jdwp::VMVirtualMachine:: +getSourceDebugExtension (MAYBE_UNUSED jclass klass) { return NULL; } +jbyteArray +gnu::classpath::jdwp::VMVirtualMachine:: +getBytecodes (MAYBE_UNUSED gnu::classpath::jdwp::VMMethod *method) +{ + return NULL; +} + +gnu::classpath::jdwp::util::MonitorInfo * +gnu::classpath::jdwp::VMVirtualMachine:: +getMonitorInfo (MAYBE_UNUSED jobject obj) +{ + return NULL; +} + +jobjectArray +gnu::classpath::jdwp::VMVirtualMachine:: +getOwnedMonitors (MAYBE_UNUSED ::java::lang::Thread *thread) +{ + return NULL; +} + +jobject +gnu::classpath::jdwp::VMVirtualMachine:: +getCurrentContendedMonitor (MAYBE_UNUSED ::java::lang::Thread *thread) +{ + return NULL; +} + +void +gnu::classpath::jdwp::VMVirtualMachine:: +popFrames (MAYBE_UNUSED ::java::lang::Thread *thread, + MAYBE_UNUSED jlong frameId) +{ +} + +// A simple caching function used while single-stepping +static jvmtiError +get_linetable (jvmtiEnv *env, jmethodID method, jint *count_ptr, + jvmtiLineNumberEntry **table_ptr) +{ + static jint last_count = 0; + static jvmtiLineNumberEntry *last_table = NULL; + static jmethodID last_method = 0; + + if (method == last_method) + { + *count_ptr = last_count; + *table_ptr = last_table; + return JVMTI_ERROR_NONE; + } + + jvmtiError err; + jint count; + jvmtiLineNumberEntry *table; + err = env->GetLineNumberTable (method, &count, &table); + if (err != JVMTI_ERROR_NONE) + { + // Keep last table in cache + return err; + } + + env->Deallocate ((unsigned char *) last_table); + last_table = *table_ptr = table; + last_count = *count_ptr = count; + return JVMTI_ERROR_NONE; +} + +static gnu::classpath::jdwp::event::filters::StepFilter * +get_request_step_filter (EventRequest *request) +{ + ::java::util::Collection *filters = request->getFilters (); + ::java::util::Iterator *iter = filters->iterator (); + filters::StepFilter *filter = NULL; + while (iter->hasNext ()) + { + using namespace gnu::classpath::jdwp::event::filters; + IEventFilter *next = (IEventFilter *) iter->next (); + if (next->getClass () == &StepFilter::class$) + { + filter = reinterpret_cast<StepFilter *> (next); + break; + } + } + + return filter; +} + +static Location * +get_request_location (EventRequest *request) +{ + Location *loc = NULL; + ::java::util::Collection *filters = request->getFilters (); + ::java::util::Iterator *iter = filters->iterator (); + while (iter->hasNext ()) + { + using namespace gnu::classpath::jdwp::event::filters; + IEventFilter *filter = (IEventFilter *) iter->next (); + if (filter->getClass () == &LocationOnlyFilter::class$) + { + LocationOnlyFilter *lof + = reinterpret_cast<LocationOnlyFilter *> (filter); + loc = lof->getLocation (); + } + } + + return loc; +} + static void +handle_single_step (jvmtiEnv *env, struct step_info *sinfo, jthread thread, + jmethodID method, jlocation location) +{ + using namespace gnu::classpath::jdwp; + + if (sinfo == NULL || sinfo->size == JdwpConstants$StepSize::MIN) + { + // Stop now + goto send_notification; + } + else + { + // Check if we're on a new source line + /* This is a little inefficient when we're stepping OVER, + but this must be done when stepping INTO. */ + jint count; + jvmtiLineNumberEntry *table; + if (get_linetable (env, method, &count, &table) == JVMTI_ERROR_NONE) + { + jint i; + for (i = 0; i < count; ++i) + { + if (table[i].start_location == location) + { + // This is the start of a new line -- stop + goto send_notification; + } + } + + // Not at a new source line -- just keep stepping + return; + } + else + { + /* Something went wrong: either "absent information" + or "out of memory" ("invalid method id" and "native + method" aren't possible -- those are validated before + single stepping is enabled). + + Do what gdb does: just keep going. */ + return; + } + } + + send_notification: + jclass klass; + jvmtiError err = env->GetMethodDeclaringClass (method, &klass); + if (err != JVMTI_ERROR_NONE) + { + fprintf (stderr, "libgcj: internal error: could not find class for method while single stepping -- continuing\n"); + return; + } + + VMMethod *vmmethod = new VMMethod (klass, reinterpret_cast<jlong> (method)); + Location *loc = new Location (vmmethod, location); + JvAssert (thread->frame.frame_type == frame_interpreter); + _Jv_InterpFrame *iframe + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + jobject instance = iframe->get_this_ptr (); + event::SingleStepEvent *event + = new event::SingleStepEvent (thread, loc, instance); + + // We only want to send the notification (and consequently + // suspend) if we are not about to execute a breakpoint. + _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (iframe->self); + if (im->breakpoint_at (location)) + { + // Next insn is a breakpoint -- record event and + // wait for the JVMTI breakpoint notification to + // enforce a suspension policy. + VMVirtualMachine::_event_list->add (event); + } + else + { + // Next insn is not a breakpoint, so send notification + // and enforce the suspend policy. + Jdwp::notify (event); + } +} + +static void +throw_jvmti_error (jvmtiError err) +{ + char *error; + jstring msg; + if (_jdwp_jvmtiEnv->GetErrorName (err, &error) == JVMTI_ERROR_NONE) + { + msg = JvNewStringLatin1 (error); + _jdwp_jvmtiEnv->Deallocate ((unsigned char *) error); + } + else + msg = JvNewStringLatin1 ("out of memory"); + + using namespace gnu::classpath::jdwp::exception; + throw new JdwpInternalErrorException (msg); +} + +static void JNICALL +jdwpBreakpointCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, + jthread thread, jmethodID method, jlocation location) +{ + jclass klass; + jvmtiError err; + err = env->GetMethodDeclaringClass (method, &klass); + JvAssert (err == JVMTI_ERROR_NONE); + + using namespace gnu::classpath::jdwp; + using namespace gnu::classpath::jdwp::event; + + jlong methodId = reinterpret_cast<jlong> (method); + VMMethod *meth = VMVirtualMachine::getClassMethod (klass, methodId); + Location *loc = new Location (meth, location); + JvAssert (thread->frame.frame_type == frame_interpreter); + _Jv_InterpFrame *iframe + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + jobject instance = iframe->get_this_ptr (); + BreakpointEvent *event = new BreakpointEvent (thread, loc, instance); + + VMVirtualMachine::_event_list->add (event); + JArray<Event *> *events + = ((JArray<Event *> *) + JvNewObjectArray (VMVirtualMachine::_event_list->size (), + &Event::class$, NULL)); + VMVirtualMachine::_event_list->toArray ((jobjectArray) events); + VMVirtualMachine::_event_list->clear (); + Jdwp::notify (events); +} + +static void JNICALL +jdwpClassPrepareCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, + jthread thread, jclass klass) +{ + using namespace gnu::classpath::jdwp; + + jint status = VMVirtualMachine::getClassStatus (klass); + event::ClassPrepareEvent *event + = new event::ClassPrepareEvent (thread, klass, status); + Jdwp::notify (event); +} + +static void JNICALL +jdwpExceptionCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread, + jmethodID method, jlocation location, jobject exception, + jmethodID catch_method, jlocation catch_location) +{ + using namespace gnu::classpath::jdwp; + jclass throw_klass; + jvmtiError err = env->GetMethodDeclaringClass (method, &throw_klass); + if (err != JVMTI_ERROR_NONE) + { + fprintf (stderr, "libgcj: internal error: could not find class for "); + fprintf (stderr, "method throwing exception -- continuing\n"); + return; + } + + VMMethod *vmmethod = new VMMethod (throw_klass, + reinterpret_cast<jlong> (method)); + Location *throw_loc = new Location (vmmethod, location); + Location *catch_loc = NULL; + if (catch_method == 0) + catch_loc = Location::getEmptyLocation (); + else + { + jclass catch_klass; + err = env->GetMethodDeclaringClass (catch_method, &catch_klass); + if (err != JVMTI_ERROR_NONE) + { + fprintf (stderr, + "libgcj: internal error: could not find class for "); + fprintf (stderr, + "method catching exception -- ignoring\n"); + } + else + { + vmmethod = new VMMethod (catch_klass, + reinterpret_cast<jlong> (catch_method)); + catch_loc = new Location (vmmethod, catch_location); + } + } + + _Jv_InterpFrame *iframe + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + jobject instance = (iframe == NULL) ? NULL : iframe->get_this_ptr (); + Throwable *throwable = reinterpret_cast<Throwable *> (exception); + event::ExceptionEvent *e = new ExceptionEvent (throwable, thread, + throw_loc, catch_loc, + throw_klass, instance); + Jdwp::notify (e); +} + +static void JNICALL +jdwpSingleStepCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread, + jmethodID method, jlocation location) +{ + jobject si = + gnu::classpath::jdwp::VMVirtualMachine::_stepping_threads->get (thread); + struct step_info *sinfo = reinterpret_cast<struct step_info *> (si); + + if (sinfo == NULL) + { + // no step filter for this thread - simply report it + handle_single_step (env, NULL, thread, method, location); + } + else + { + // A step filter exists for this thread + using namespace gnu::classpath::jdwp; + + _Jv_InterpFrame *frame + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + + switch (sinfo->depth) + { + case JdwpConstants$StepDepth::INTO: + /* This is the easy case. We ignore the method and + simply stop at either the next insn, or the next source + line. */ + handle_single_step (env, sinfo, thread, method, location); + break; + + case JdwpConstants$StepDepth::OVER: + /* This is also a pretty easy case. We just make sure that + the methods are the same and that we are at the same + stack depth, but we should also stop on the next + insn/line if the stack depth is LESS THAN it was when + we started stepping. */ + if (method == sinfo->method) + { + // Still in the same method -- must be at same stack depth + // to avoid confusion with recursive methods. + if (frame->depth () == sinfo->stack_depth) + handle_single_step (env, sinfo, thread, method, location); + } + else if (frame->depth () < sinfo->stack_depth) + { + // The method in which we were stepping was popped off + // the stack. We simply need to stop at the next insn/line. + handle_single_step (env, sinfo, thread, method, location); + } + break; + + case JdwpConstants$StepDepth::OUT: + // All we need to do is check the stack depth + if (sinfo->stack_depth > frame->depth ()) + handle_single_step (env, sinfo, thread, method, location); + break; + + default: + /* This should not happen. The JDWP back-end should have + validated the StepFilter. */ + fprintf (stderr, + "libgcj: unknown step depth while single stepping\n"); + return; + } + } +} + +static void JNICALL +jdwpThreadEndCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, + jthread thread) +{ + using namespace gnu::classpath::jdwp::event; + + ThreadEndEvent *e = new ThreadEndEvent (thread); + gnu::classpath::jdwp::Jdwp::notify (e); +} + +static void JNICALL +jdwpThreadStartCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, + jthread thread) +{ + using namespace gnu::classpath::jdwp::event; + + ThreadStartEvent *e = new ThreadStartEvent (thread); + gnu::classpath::jdwp::Jdwp::notify (e); +} + +static void JNICALL +jdwpVMDeathCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env) +{ + using namespace gnu::classpath::jdwp::event; + gnu::classpath::jdwp::Jdwp::notify (new VmDeathEvent ()); +} + +static void JNICALL jdwpVMInitCB (MAYBE_UNUSED jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread) { + // The VM is now initialized, add our callbacks + jvmtiEventCallbacks callbacks; + DEFINE_CALLBACK (callbacks, Breakpoint); + DEFINE_CALLBACK (callbacks, ClassPrepare); + DEFINE_CALLBACK (callbacks, Exception); + DEFINE_CALLBACK (callbacks, SingleStep); + DEFINE_CALLBACK (callbacks, ThreadEnd); + DEFINE_CALLBACK (callbacks, ThreadStart); + DEFINE_CALLBACK (callbacks, VMDeath); + _jdwp_jvmtiEnv->SetEventCallbacks (&callbacks, sizeof (callbacks)); + + // Enable callbacks + ENABLE_EVENT (BREAKPOINT, NULL); + ENABLE_EVENT (CLASS_PREPARE, NULL); + ENABLE_EVENT (EXCEPTION, NULL); + // SingleStep is enabled only when needed + ENABLE_EVENT (THREAD_END, NULL); + ENABLE_EVENT (THREAD_START, NULL); + ENABLE_EVENT (VM_DEATH, NULL); + // Send JDWP VMInit using namespace gnu::classpath::jdwp::event; - Thread *init_thread = reinterpret_cast<Thread *> (thread); - gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (init_thread)); + gnu::classpath::jdwp::Jdwp::notify (new VmInitEvent (thread)); } diff --git a/libjava/gnu/gcj/jvmti/BreakpointManager.java b/libjava/gnu/gcj/jvmti/BreakpointManager.java index 205bf6d8a5b..5ef1b08d4df 100644 --- a/libjava/gnu/gcj/jvmti/BreakpointManager.java +++ b/libjava/gnu/gcj/jvmti/BreakpointManager.java @@ -1,6 +1,6 @@ // BreakpointManager.java - A convenience class for dealing with breakpoints -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -45,6 +45,7 @@ public class BreakpointManager { Breakpoint bp = new Breakpoint (method, location); Location loc = new Location (method, location); + bp.install (); _instance._breakpoints.put (loc, bp); return bp; } @@ -58,7 +59,12 @@ public class BreakpointManager public static void deleteBreakpoint (long method, long location) { Location loc = new Location (method, location); - _instance._breakpoints.remove (loc); + Breakpoint bp = (Breakpoint) _instance._breakpoints.get (loc); + if (bp != null) + { + bp.remove (); + _instance._breakpoints.remove (loc); + } } /** diff --git a/libjava/gnu/gcj/jvmti/natBreakpoint.cc b/libjava/gnu/gcj/jvmti/natBreakpoint.cc index ab9de417bb8..5dbd3f834dd 100644 --- a/libjava/gnu/gcj/jvmti/natBreakpoint.cc +++ b/libjava/gnu/gcj/jvmti/natBreakpoint.cc @@ -1,6 +1,6 @@ // natBreakpoint.cc - C++ side of Breakpoint -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -17,6 +17,7 @@ details. */ #include <jvmti.h> #include <gnu/gcj/jvmti/Breakpoint.h> +#include <gnu/gcj/jvmti/BreakpointManager.h> static _Jv_InterpMethod * get_interp_method (jlong method) @@ -39,7 +40,6 @@ gnu::gcj::jvmti::Breakpoint::initialize_native () pc_t code = imeth->get_insn (location); data = (RawDataManaged *) JvAllocBytes (sizeof (*code)); memcpy (data, code, sizeof (*code)); - install (); } void @@ -55,3 +55,18 @@ gnu::gcj::jvmti::Breakpoint::remove () _Jv_InterpMethod *imeth = get_interp_method (method); imeth->set_insn (location, reinterpret_cast<pc_t> (data)); } + +#ifdef DIRECT_THREADED +void +_Jv_RewriteBreakpointInsn (jmethodID mid, jlocation loc, pc_t new_insn) +{ + using namespace ::gnu::gcj::jvmti; + Breakpoint *bp + = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (mid), loc); + if (bp != NULL) + { + pc_t old_insn = (pc_t) bp->data; + old_insn->insn = new_insn; + } +} +#endif // DIRECT_THREADED diff --git a/libjava/headers.txt b/libjava/headers.txt index 3e08f175889..cc79b771b3d 100644 --- a/libjava/headers.txt +++ b/libjava/headers.txt @@ -65,5 +65,13 @@ friend class java::io::ObjectInputStream; friend java::lang::reflect::Method* ::_Jv_GetReflectedMethod (jclass, _Jv_Utf8Const*, _Jv_Utf8Const*); friend java::lang::reflect::Method* ::_Jv_LookupProxyMethod (jclass, _Jv_Utf8Const *, _Jv_Utf8Const *); +class gnu/gcj/jvmti/Breakpoint +prepend #ifdef DIRECT_THREADED +prepend void _Jv_RewriteBreakpointInsn (jmethodID, jlocation, pc_t); +prepend #endif +add #ifdef DIRECT_THREADED +add friend void (::_Jv_RewriteBreakpointInsn (jmethodID, jlocation, pc_t)); +add #endif + class gnu/gcj/runtime/ExtensionClassLoader friend class ::java::lang::ClassLoader; diff --git a/libjava/include/boehm-gc.h b/libjava/include/boehm-gc.h index 7e61b8e48fb..ed8ac6aa2e3 100644 --- a/libjava/include/boehm-gc.h +++ b/libjava/include/boehm-gc.h @@ -1,7 +1,7 @@ // -*- c++ -*- // boehm-gc.h - Defines for Boehm collector. -/* Copyright (C) 1998, 1999, 2002, 2004, 2006 Free Software Foundation +/* Copyright (C) 1998, 1999, 2002, 2004, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -93,4 +93,6 @@ extern "C" void _Jv_SuspendThread (_Jv_Thread_t *); // Resume a suspended thread. extern "C" void _Jv_ResumeThread (_Jv_Thread_t *); +// Is the given thread suspended? +extern "C" int _Jv_IsThreadSuspended (_Jv_Thread_t *); #endif /* __JV_BOEHM_GC__ */ diff --git a/libjava/include/java-interp.h b/libjava/include/java-interp.h index ae93109ef7b..52a04e78e1d 100644 --- a/libjava/include/java-interp.h +++ b/libjava/include/java-interp.h @@ -1,6 +1,6 @@ // java-interp.h - Header file for the bytecode interpreter. -*- c++ -*- -/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation +/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -137,6 +137,21 @@ struct _Jv_LineTableEntry int line; }; +// This structure holds local variable information. +// The pc value is the first pc where the variable must have a value and it +// must continue to have a value until (start_pc + length). +// The name is the variable name, and the descriptor contains type information. +// The slot is the index in the local variable array of this method, long and +// double occupy slot and slot+1. +struct _Jv_LocalVarTableEntry +{ + int bytecode_start_pc; + int length; + char *name; + char *descriptor; + int slot; +}; + class _Jv_InterpMethod : public _Jv_MethodBase { // Breakpoint instruction @@ -157,6 +172,10 @@ class _Jv_InterpMethod : public _Jv_MethodBase // Length of the line_table - when this is zero then line_table is NULL. int line_table_len; _Jv_LineTableEntry *line_table; + + // The local variable table length and the table itself + int local_var_table_len; + _Jv_LocalVarTableEntry *local_var_table; pc_t prepared; int number_insn_slots; @@ -205,11 +224,16 @@ class _Jv_InterpMethod : public _Jv_MethodBase // number info is unavailable. int get_source_line(pc_t mpc); + public: + // Convenience function for indexing bytecode PC/insn slots in // line tables for JDWP jlong insn_index (pc_t pc); - public: + // Helper function used to check if there is a handler for an exception + // present at this code index + jboolean check_handler (pc_t *pc, _Jv_InterpMethod *meth, + java::lang::Throwable *ex); /* Get the line table for this method. * start is the lowest index in the method @@ -219,6 +243,25 @@ class _Jv_InterpMethod : public _Jv_MethodBase */ void get_line_table (jlong& start, jlong& end, jintArray& line_numbers, jlongArray& code_indices); + + int get_max_locals () + { + return static_cast<int> (max_locals); + } + + /* Get info for a local variable of this method. + * If there is no loca_var_table for this method it will return -1. + * table_slot indicates which slot in the local_var_table to get, if there is + * no variable at this location it will return 0. + * Otherwise, it will return the number of table slots after the selected + * slot, indexed from 0. + * + * Example: there are 5 slots in the table, you request slot 0 so it will + * return 4. + */ + int get_local_var_table (char **name, char **sig, char **generic_sig, + jlong *startloc, jint *length, jint *slot, + int table_slot); /* Installs a break instruction at the given code index. Returns the pc_t of the breakpoint or NULL if index is invalid. */ @@ -231,6 +274,9 @@ class _Jv_InterpMethod : public _Jv_MethodBase the insn or NULL if index is invalid. */ pc_t set_insn (jlong index, pc_t insn); + // Is the given location in this method a breakpoint? + bool breakpoint_at (jlong index); + #ifdef DIRECT_THREADED friend void _Jv_CompileMethod (_Jv_InterpMethod*); #endif @@ -263,6 +309,7 @@ class _Jv_InterpClass #endif friend _Jv_MethodBase ** _Jv_GetFirstMethod (_Jv_InterpClass *klass); + friend jstring _Jv_GetInterpClassSourceFile (jclass); }; extern inline _Jv_MethodBase ** @@ -316,35 +363,123 @@ public: } }; -// The interpreted call stack, represented by a linked list of frames. -struct _Jv_InterpFrame +enum _Jv_FrameType +{ + frame_native, + frame_interpreter, + frame_proxy +}; + +// The composite call stack as represented by a linked list of frames +class _Jv_Frame { +public: + java::lang::Thread *thread; + union { + _Jv_MethodBase *self; void *meth; - _Jv_InterpMethod *self; _Jv_Method *proxyMethod; }; - java::lang::Thread *thread; - _Jv_InterpFrame *next; + + //The full list of frames, JNI and interpreted + _Jv_Frame *next; + _Jv_FrameType frame_type; + + _Jv_Frame (_Jv_MethodBase *s, java::lang::Thread *thr, _Jv_FrameType type) + { + self = s; + frame_type = type; + next = (_Jv_Frame *) thr->frame; + thr->frame = (gnu::gcj::RawData *) this; + thread = thr; + } + + ~_Jv_Frame () + { + thread->frame = (gnu::gcj::RawData *) next; + } + + int depth () + { + int depth = 0; + struct _Jv_Frame *f; + for (f = this; f != NULL; f = f->next) + ++depth; + + return depth; + } +}; + +// An interpreted frame in the call stack +class _Jv_InterpFrame : public _Jv_Frame +{ +public: + + // Keep the purely interpreted list around so as not to break backtraces + _Jv_InterpFrame *next_interp; + union { pc_t pc; jclass proxyClass; }; - _Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyClass = NULL) + // Pointer to the actual pc value. + pc_t *pc_ptr; + + //Debug info for local variables. + _Jv_word *locals; + char *locals_type; + + // Object pointer for this frame ("this") + jobject obj_ptr; + + _Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyCls = NULL, + pc_t *pc = NULL) + : _Jv_Frame (reinterpret_cast<_Jv_MethodBase *> (meth), thr, + frame_interpreter) { - this->meth = meth; - thread = thr; - next = (_Jv_InterpFrame *) thr->interp_frame; + next_interp = (_Jv_InterpFrame *) thr->interp_frame; + proxyClass = proxyCls; thr->interp_frame = (gnu::gcj::RawData *) this; - this->proxyClass = proxyClass; + obj_ptr = NULL; + pc_ptr = pc; } ~_Jv_InterpFrame () { - thread->interp_frame = (gnu::gcj::RawData *) next; + thread->interp_frame = (gnu::gcj::RawData *) next_interp; + } + + jobject get_this_ptr () + { + return obj_ptr; + } + + pc_t get_pc () + { + pc_t pc; + + // If the PC_PTR is NULL, we are not debugging. + if (pc_ptr == NULL) + pc = 0; + else + pc = *pc_ptr; + + return pc - 1; + } +}; + +// A native frame in the call stack really just a placeholder +class _Jv_NativeFrame : public _Jv_Frame +{ +public: + + _Jv_NativeFrame (_Jv_JNIMethod *s, java::lang::Thread *thr) + : _Jv_Frame (s, thr, frame_native) + { } }; diff --git a/libjava/include/java-stack.h b/libjava/include/java-stack.h index d4d63d74342..49e68412be6 100644 --- a/libjava/include/java-stack.h +++ b/libjava/include/java-stack.h @@ -41,13 +41,6 @@ extern "Java" } } -enum _Jv_FrameType -{ - frame_native, - frame_interpreter, - frame_proxy -}; - #ifdef INTERPRETER struct _Jv_InterpFrameInfo { diff --git a/libjava/include/jvmti-int.h b/libjava/include/jvmti-int.h index f88b4ce0a3a..6b09c837659 100644 --- a/libjava/include/jvmti-int.h +++ b/libjava/include/jvmti-int.h @@ -1,5 +1,5 @@ /* jvmti-int.h -- Internal JVMTI definitions - Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -37,6 +37,10 @@ executable file might be covered by the GNU General Public License. */ False means no JVMTI environment requested that event type. */ namespace JVMTI { + // Is JVMTI enabled? (i.e., any jvmtiEnv created?) + extern bool enabled; + + // Event notifications extern bool VMInit; extern bool VMDeath; extern bool ThreadStart; @@ -82,4 +86,9 @@ namespace JVMTI For speed, this function should only be called after JVMTI_REQUESTED_EVENT is checked. */ extern void _Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...); +// Returns the jvmtiEnv used by the JDWP backend +extern jvmtiEnv *_Jv_GetJDWP_JVMTIEnv (void); + +// Reports JVMTI excpetions +extern void _Jv_ReportJVMTIExceptionThrow (jthrowable); #endif /* __GCJ_JVMTI_INT_H__ */ diff --git a/libjava/include/jvmti_md.h b/libjava/include/jvmti_md.h index 549d42151d3..2270f3f3d16 100644 --- a/libjava/include/jvmti_md.h +++ b/libjava/include/jvmti_md.h @@ -1,5 +1,5 @@ /* jvmti_md.h - Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006, 2007 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -53,6 +53,14 @@ executable file might be covered by the GNU General Public License. */ /* One for each callback. */ \ bool enabled[EVENT_SLOTS]; +/* Redefine the standard JVMTI types to something a little more + precise than "jobject". */ +#define _CLASSPATH_VM_JVMTI_TYPES_DEFINED +typedef java::lang::Thread *jthread; +typedef java::lang::ThreadGroup *jthreadGroup; +typedef jlong jlocation; +typedef struct _Jv_rawMonitorID *jrawMonitorID; + #endif /* __GCJ_JNI_IMPL__ */ #endif /* __GCJ_JVMTI_MD_H__ */ diff --git a/libjava/include/no-gc.h b/libjava/include/no-gc.h index 193b8ea4d05..ce0ffb81017 100644 --- a/libjava/include/no-gc.h +++ b/libjava/include/no-gc.h @@ -1,7 +1,7 @@ // -*- c++ -*- // no-gc.h - Defines for no garbage collector. -/* Copyright (C) 1998, 1999, 2006 Free Software Foundation +/* Copyright (C) 1998, 1999, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -18,4 +18,6 @@ extern "C" void _Jv_SuspendThread (_Jv_Thread_t *); // Resume a suspended thread. extern "C" void _Jv_ResumeThread (_Jv_Thread_t *); +// Is the given thread suspended? +extern "C" int _Jv_IsThreadSuspended (_Jv_Thread_t *); #endif /* __JV_NO_GC__ */ diff --git a/libjava/include/win32-threads.h b/libjava/include/win32-threads.h index 442149c3cd8..b8f70f5cba9 100644 --- a/libjava/include/win32-threads.h +++ b/libjava/include/win32-threads.h @@ -227,5 +227,6 @@ private: #undef STRICT #undef VOID #undef TEXT +#undef OUT #endif /* __JV_WIN32_THREADS__ */ diff --git a/libjava/interpret-run.cc b/libjava/interpret-run.cc index 26cc4a616dd..9be08df488b 100644 --- a/libjava/interpret-run.cc +++ b/libjava/interpret-run.cc @@ -1,6 +1,6 @@ // interpret-run.cc - Code to interpret bytecode -/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation +/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -12,6 +12,8 @@ details. */ * compiled directly. */ using namespace java::lang::reflect; + + pc_t pc = NULL; // FRAME_DESC registers this particular invocation as the top-most // interpreter frame. This lets the stack tracing code (for @@ -20,13 +22,108 @@ details. */ // destructor so it cleans up automatically when the interpreter // returns. java::lang::Thread *thread = java::lang::Thread::currentThread(); + +#ifdef DEBUG + _Jv_InterpFrame frame_desc (meth, thread, NULL, &pc); +#else _Jv_InterpFrame frame_desc (meth, thread); +#endif _Jv_word stack[meth->max_stack]; _Jv_word *sp = stack; _Jv_word locals[meth->max_locals]; +#ifdef DEBUG + // This is the information needed to get and set local variables with + // proper type checking. + frame_desc.locals = locals; + char locals_type[meth->max_locals]; + frame_desc.locals_type = locals_type; + + // Set all slots as invalid until they are written to. + memset (locals_type, 'x', meth->max_locals); + + // We need to set the local variable types for the method arguments since + // they are valid at invocation. + + _Jv_Method *method = meth->get_method (); + int type_ctr = 0; + + // If the method is non-static, we need to set the type for the "this" pointer. + if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0) + { + if (args) + { + // Set the "this" pointer for this frame. + _Jv_word *this_ptr = reinterpret_cast<_Jv_word *> (args); + frame_desc.obj_ptr = this_ptr[0].o; + } + + frame_desc.locals_type[0] = 'o'; + type_ctr++; + } + + // Now parse the method signature to set the types of the other arguments. + int sig_len = method->signature->len (); + char *signature = method->signature->chars (); + for (int i = 1; signature[i] != ')' && i <= sig_len; i++) + { + if (signature[i] == 'Z' || signature[i] == 'B' || signature[i] == 'C' + || signature[i] == 'S' || signature[i] == 'I') + { + frame_desc.locals_type[type_ctr] = 'i'; + type_ctr++; + continue; + } + else if (signature[i] == 'F') + { + frame_desc.locals_type[type_ctr] = 'f'; + type_ctr++; + continue; + } + else if (signature[i] == 'J') + { + frame_desc.locals_type[type_ctr] = 'l'; + frame_desc.locals_type[type_ctr+1] = 'x'; + type_ctr += 2; + continue; + } + else if (signature[i] == 'D') + { + frame_desc.locals_type[type_ctr] = 'd'; + frame_desc.locals_type[type_ctr+1] = 'x'; + type_ctr += 2; + continue; + } + else if (signature[i] == 'L') + { + frame_desc.locals_type[type_ctr] = 'o'; + type_ctr++; + while (signature[i] != ';') + i++; + continue; + } + else if (signature[i] == '[') + { + frame_desc.locals_type[type_ctr] = 'o'; + type_ctr++; + + // Ignore multi-dimensional arrays. + while (signature[i] == '[') + i++; + + // Check for an object array + if (signature[i] == 'L') + { + while (signature[i] != ';') + i++; + } + continue; + } + } +#endif /* DEBUG */ + #define INSN_LABEL(op) &&insn_##op static const void *const insn_target[] = @@ -217,7 +314,7 @@ details. */ INSN_LABEL(invokespecial), INSN_LABEL(invokestatic), INSN_LABEL(invokeinterface), - INSN_LABEL (breakpoint), + INSN_LABEL(breakpoint), INSN_LABEL(new), INSN_LABEL(newarray), INSN_LABEL(anewarray), @@ -244,11 +341,58 @@ details. */ #endif }; - pc_t pc; - #ifdef DIRECT_THREADED +#ifdef DEBUG +#undef NEXT_INSN +#define NEXT_INSN \ + do \ + { \ + pc_t insn = pc++; \ + if (JVMTI_REQUESTED_EVENT (SingleStep)) \ + { \ + JNIEnv *env = _Jv_GetCurrentJNIEnv (); \ + jmethodID method = meth->self; \ + jlocation loc = meth->insn_index (insn); \ + _Jv_JVMTI_PostEvent (JVMTI_EVENT_SINGLE_STEP, thread, \ + env, method, loc); \ + } \ + goto *(insn->insn); \ + } \ + while (0) + +#undef REWRITE_INSN +#define REWRITE_INSN(INSN,SLOT,VALUE) \ + do { \ + if (pc[-2].insn == breakpoint_insn->insn) \ + { \ + using namespace ::gnu::gcj::jvmti; \ + jlocation location = meth->insn_index (pc - 2); \ + _Jv_RewriteBreakpointInsn (meth->self, location, (pc_t) INSN); \ + } \ + else \ + pc[-2].insn = INSN; \ + \ + pc[-1].SLOT = VALUE; \ + } \ + while (0) + +#undef INTERP_REPORT_EXCEPTION +#define INTERP_REPORT_EXCEPTION(Jthrowable) REPORT_EXCEPTION (Jthrowable) +#else // !DEBUG +#undef NEXT_INSN #define NEXT_INSN goto *((pc++)->insn) +#define REWRITE_INSN(INSN,SLOT,VALUE) \ + do { \ + pc[-2].insn = INSN; \ + pc[-1].SLOT = VALUE; \ + } \ + while (0) + +#undef INTERP_REPORT_EXCEPTION +#define INTERP_REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */ +#endif // !DEBUG + #define INTVAL() ((pc++)->int_val) #define AVAL() ((pc++)->datum) @@ -281,7 +425,22 @@ details. */ #else +#ifdef DEBUG +#define NEXT_INSN \ + do \ + { \ + if (JVMTI_REQUESTED_EVENT (SingleStep)) \ + { \ + JNIEnv *env = _Jv_GetCurrentJNIEnv (); \ + jmethodID method = meth->self; \ + jlocation loc = meth->insn_index (pc); \ + _Jv_JVMTI_PostEvent (JVMTI_EVENT_SINGLE_STEP, thread, \ + env, method, loc); \ + } \ + goto *(insn_target[*pc++]) +#else #define NEXT_INSN goto *(insn_target[*pc++]) +#endif #define GET1S() get1s (pc++) #define GET2S() (pc += 2, get2s (pc- 2)) @@ -381,8 +540,7 @@ details. */ #ifdef DIRECT_THREADED // Rewrite instruction so that we use a faster pre-resolved // method. - pc[-2].insn = &&invokevirtual_resolved; - pc[-1].datum = rmeth; + REWRITE_INSN (&&invokevirtual_resolved, datum, rmeth); #endif /* DIRECT_THREADED */ } goto perform_invoke; @@ -1716,8 +1874,7 @@ details. */ } #ifdef DIRECT_THREADED - pc[-2].insn = newinsn; - pc[-1].datum = field->u.addr; + REWRITE_INSN (newinsn, datum, field->u.addr); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -1807,8 +1964,7 @@ details. */ } #ifdef DIRECT_THREADED - pc[-2].insn = newinsn; - pc[-1].int_val = field_offset; + REWRITE_INSN (newinsn, int_val, field_offset); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -1923,8 +2079,7 @@ details. */ } #ifdef DIRECT_THREADED - pc[-2].insn = newinsn; - pc[-1].datum = field->u.addr; + REWRITE_INSN (newinsn, datum, field->u.addr); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2022,8 +2177,7 @@ details. */ } #ifdef DIRECT_THREADED - pc[-2].insn = newinsn; - pc[-1].int_val = field_offset; + REWRITE_INSN (newinsn, int_val, field_offset); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2098,8 +2252,7 @@ details. */ #ifdef DIRECT_THREADED // Rewrite instruction so that we use a faster pre-resolved // method. - pc[-2].insn = &&invokespecial_resolved; - pc[-1].datum = rmeth; + REWRITE_INSN (&&invokespecial_resolved, datum, rmeth); #endif /* DIRECT_THREADED */ } goto perform_invoke; @@ -2136,8 +2289,7 @@ details. */ #ifdef DIRECT_THREADED // Rewrite instruction so that we use a faster pre-resolved // method. - pc[-2].insn = &&invokestatic_resolved; - pc[-1].datum = rmeth; + REWRITE_INSN (&&invokestatic_resolved, datum, rmeth); #endif /* DIRECT_THREADED */ } goto perform_invoke; @@ -2175,8 +2327,7 @@ details. */ #ifdef DIRECT_THREADED // Rewrite instruction so that we use a faster pre-resolved // method. - pc[-2].insn = &&invokeinterface_resolved; - pc[-1].datum = rmeth; + REWRITE_INSN (&&invokeinterface_resolved, datum, rmeth); #else // Skip dummy bytes. pc += 2; @@ -2209,13 +2360,16 @@ details. */ /* VM spec, section 3.11.5 */ if ((klass->getModifiers() & Modifier::ABSTRACT) || klass->isInterface()) - throw new java::lang::InstantiationException; + { + jthrowable t = new java::lang::InstantiationException; + INTERP_REPORT_EXCEPTION (t); + throw t; + } jobject res = _Jv_AllocObject (klass); PUSHA (res); #ifdef DIRECT_THREADED - pc[-2].insn = &&new_resolved; - pc[-1].datum = klass; + REWRITE_INSN (&&new_resolved, datum, klass); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2250,8 +2404,7 @@ details. */ PUSHA (result); #ifdef DIRECT_THREADED - pc[-2].insn = &&anewarray_resolved; - pc[-1].datum = klass; + REWRITE_INSN (&&anewarray_resolved, datum, klass); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2278,7 +2431,9 @@ details. */ insn_athrow: { jobject value = POPA(); - throw static_cast<jthrowable>(value); + jthrowable t = static_cast<jthrowable> (value); + INTERP_REPORT_EXCEPTION (t); + throw t; } NEXT_INSN; @@ -2295,8 +2450,7 @@ details. */ PUSHA (value); #ifdef DIRECT_THREADED - pc[-2].insn = &&checkcast_resolved; - pc[-1].datum = to; + REWRITE_INSN (&&checkcast_resolved, datum, to); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2323,8 +2477,7 @@ details. */ PUSHI (to->isInstance (value)); #ifdef DIRECT_THREADED - pc[-2].insn = &&instanceof_resolved; - pc[-1].datum = to; + REWRITE_INSN (&&instanceof_resolved, datum, to); #endif /* DIRECT_THREADED */ } NEXT_INSN; @@ -2466,47 +2619,56 @@ details. */ insn_breakpoint: { - // nothing just yet - } - } - catch (java::lang::Throwable *ex) - { -#ifdef DIRECT_THREADED - void *logical_pc = (void *) ((insn_slot *) pc - 1); -#else - int logical_pc = pc - 1 - meth->bytecode (); -#endif - _Jv_InterpException *exc = meth->exceptions (); - jclass exc_class = ex->getClass (); + JvAssert (JVMTI_REQUESTED_EVENT (Breakpoint)); - for (int i = 0; i < meth->exc_count; i++) - { - if (PCVAL (exc[i].start_pc) <= logical_pc - && logical_pc < PCVAL (exc[i].end_pc)) - { -#ifdef DIRECT_THREADED - jclass handler = (jclass) exc[i].handler_type.p; -#else - jclass handler = NULL; - if (exc[i].handler_type.i != 0) - handler = (_Jv_Linker::resolve_pool_entry (meth->defining_class, - exc[i].handler_type.i)).clazz; -#endif /* DIRECT_THREADED */ + // Send JVMTI notification + using namespace ::java::lang; + jmethodID method = meth->self; + jlocation location = meth->insn_index (pc - 1); + Thread *thread = Thread::currentThread (); + JNIEnv *jni_env = _Jv_GetCurrentJNIEnv (); - if (handler == NULL || handler->isAssignableFrom (exc_class)) - { + // Save the insn here since the breakpoint could be removed + // before the JVMTI notification returns. + using namespace gnu::gcj::jvmti; + Breakpoint *bp + = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method), + location); + JvAssert (bp != NULL); + pc_t opc = reinterpret_cast<pc_t> (bp->getInsn ()); + + _Jv_JVMTI_PostEvent (JVMTI_EVENT_BREAKPOINT, thread, jni_env, + method, location); + // Continue execution #ifdef DIRECT_THREADED - pc = (insn_slot *) exc[i].handler_pc.p; + goto *(opc->insn); #else - pc = meth->bytecode () + exc[i].handler_pc.i; -#endif /* DIRECT_THREADED */ - sp = stack; - sp++->o = ex; // Push exception. - NEXT_INSN; - } - } - } + goto *(insn_target[*opc]); +#endif + } + } + catch (java::lang::Throwable *ex) + { + // Check if the exception is handled and, if so, set the pc to the start + // of the appropriate catch block. + if (meth->check_handler (&pc, meth, ex)) + { + sp = stack; + sp++->o = ex; // Push exception. +#ifdef DEBUG + if (JVMTI_REQUESTED_EVENT (ExceptionCatch)) + { + using namespace gnu::gcj::jvmti; + jlong catch_meth = reinterpret_cast<jlong> (meth->get_method ()); + jlong catch_loc = meth->insn_index (pc); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION_CATCH, thread, + _Jv_GetCurrentJNIEnv (), catch_meth, + catch_loc, ex); + } +#endif + NEXT_INSN; + } // No handler, so re-throw. throw ex; diff --git a/libjava/interpret.cc b/libjava/interpret.cc index 7dade7eec8c..cdc4d002fc8 100644 --- a/libjava/interpret.cc +++ b/libjava/interpret.cc @@ -1,6 +1,6 @@ // interpret.cc - Code for the interpreter -/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation +/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -37,7 +37,11 @@ details. */ #include <execution.h> #include <java/lang/reflect/Modifier.h> -#include <gnu/classpath/jdwp/Jdwp.h> +#include <jvmti.h> +#include "jvmti-int.h" + +#include <gnu/gcj/jvmti/Breakpoint.h> +#include <gnu/gcj/jvmti/BreakpointManager.h> #ifdef INTERPRETER @@ -60,6 +64,16 @@ static void throw_class_format_error (jstring msg) static void throw_class_format_error (const char *msg) __attribute__ ((__noreturn__)); +static void find_catch_location (jthrowable, jthread, jmethodID *, jlong *); + +// A macro to facilitate JVMTI exception reporting +#define REPORT_EXCEPTION(Jthrowable) \ + do { \ + if (JVMTI_REQUESTED_EVENT (Exception)) \ + _Jv_ReportJVMTIExceptionThrow (Jthrowable); \ + } \ + while (0) + #ifdef DIRECT_THREADED // Lock to ensure that methods are not compiled concurrently. // We could use a finer-grained lock here, however it is not safe to use @@ -166,54 +180,81 @@ convert (FROM val, TO min, TO max) # define LOADD(I) LOADL(I) #endif -#define STOREA(I) \ -do { \ -DEBUG_LOCALS_INSN(I, 'o'); \ -locals[I].o = (--sp)->o; \ -} while(0) -#define STOREI(I) \ -do { \ -DEBUG_LOCALS_INSN (I, 'i'); \ -locals[I].i = (--sp)->i; \ -} while(0) -#define STOREF(I) \ -do { \ -DEBUG_LOCALS_INSN (I, 'f'); \ -locals[I].f = (--sp)->f; \ -} while(0) +#define STOREA(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'o'); \ + locals[__idx].o = (--sp)->o; \ + } \ + while (0) +#define STOREI(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'i'); \ + locals[__idx].i = (--sp)->i; \ + } while (0) +#define STOREF(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'f'); \ + locals[__idx].f = (--sp)->f; \ + } \ + while (0) #if SIZEOF_VOID_P == 8 -# define STOREL(I) \ -do { \ -DEBUG_LOCALS_INSN (I, 'l'); \ -(sp -= 2, locals[I].l = sp->l); \ -} while(0) -# define STORED(I) \ -do { \ -DEBUG_LOCALS_INSN (I, 'd'); \ -(sp -= 2, locals[I].d = sp->d); \ -} while(0) +# define STOREL(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'l'); \ + DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ + (sp -= 2, locals[__idx].l = sp->l); \ + } \ + while (0) +# define STORED(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'd'); \ + DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ + (sp -= 2, locals[__idx].d = sp->d); \ + } \ + while (0) #else -# define STOREL(I) \ -do { DEBUG_LOCALS_INSN(I, 'l'); \ - jint __idx = (I); \ - locals[__idx+1].ia[0] = (--sp)->ia[0]; \ - locals[__idx].ia[0] = (--sp)->ia[0]; \ - } while (0) -# define STORED(I) \ -do { DEBUG_LOCALS_INSN(I, 'd'); \ - jint __idx = (I); \ - locals[__idx+1].ia[0] = (--sp)->ia[0]; \ - locals[__idx].ia[0] = (--sp)->ia[0]; \ - } while (0) +# define STOREL(I) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'l'); \ + DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ + locals[__idx + 1].ia[0] = (--sp)->ia[0]; \ + locals[__idx].ia[0] = (--sp)->ia[0]; \ + } \ + while (0) +# define STORED(I) \ + do { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'd'); \ + DEBUG_LOCALS_INSN (__idx + 1, 'x'); \ + locals[__idx + 1].ia[0] = (--sp)->ia[0]; \ + locals[__idx].ia[0] = (--sp)->ia[0]; \ + } while (0) #endif #define PEEKI(I) (locals+(I))->i #define PEEKA(I) (locals+(I))->o -#define POKEI(I,V) \ -DEBUG_LOCALS_INSN(I,'i'); \ -((locals+(I))->i = (V)) +#define POKEI(I,V) \ + do \ + { \ + jint __idx = (I); \ + DEBUG_LOCALS_INSN (__idx, 'i'); \ + ((locals + __idx)->i = (V)); \ + } \ + while (0) #define BINOPI(OP) { \ @@ -916,18 +957,14 @@ _Jv_InterpMethod::run (void *retp, ffi_raw *args, _Jv_InterpMethod *meth) void _Jv_InterpMethod::run_debug (void *retp, ffi_raw *args, _Jv_InterpMethod *meth) { -/* Used to keep track of local variable type - * - * Possible Types: - * o object - * i integer - * f float - * l long - * d double - */ #define DEBUG #undef DEBUG_LOCALS_INSN -#define DEBUG_LOCALS_INSN(s, t) do {} while(0) +#define DEBUG_LOCALS_INSN(s, t) \ + do \ + { \ + frame_desc.locals_type[s] = t; \ + } \ + while (0) #include "interpret-run.cc" } @@ -935,19 +972,25 @@ _Jv_InterpMethod::run_debug (void *retp, ffi_raw *args, _Jv_InterpMethod *meth) static void throw_internal_error (const char *msg) { - throw new java::lang::InternalError (JvNewStringLatin1 (msg)); + jthrowable t = new java::lang::InternalError (JvNewStringLatin1 (msg)); + REPORT_EXCEPTION (t); + throw t; } static void throw_incompatible_class_change_error (jstring msg) { - throw new java::lang::IncompatibleClassChangeError (msg); + jthrowable t = new java::lang::IncompatibleClassChangeError (msg); + REPORT_EXCEPTION (t); + throw t; } static void throw_null_pointer_exception () { - throw new java::lang::NullPointerException; + jthrowable t = new java::lang::NullPointerException; + REPORT_EXCEPTION (t); + throw t; } /* Look up source code line number for given bytecode (or direct threaded @@ -1290,34 +1333,34 @@ _Jv_InterpMethod::ncode (jclass klass) { if (staticp) { - if (::gnu::classpath::jdwp::Jdwp::isDebugging) - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class_debug; - else - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class; + if (JVMTI::enabled) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class_debug; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class; } else { - if (::gnu::classpath::jdwp::Jdwp::isDebugging) - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object_debug; - else - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; - } + if (JVMTI::enabled) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object_debug; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_object; + } } else { if (staticp) { - if (::gnu::classpath::jdwp::Jdwp::isDebugging) - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class_debug; - else - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class; + if (JVMTI::enabled) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class_debug; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_class; } else { - if (::gnu::classpath::jdwp::Jdwp::isDebugging) - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal_debug; - else - fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal; + if (JVMTI::enabled) + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal_debug; + else + fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal; } } @@ -1361,6 +1404,51 @@ _Jv_InterpMethod::insn_index (pc_t pc) return -1; } +// Method to check if an exception is caught at some location in a method +// (meth). Returns true if this method (meth) contains a catch block for the +// exception (ex). False otherwise. If there is a catch block, it sets the pc +// to the location of the beginning of the catch block. +jboolean +_Jv_InterpMethod::check_handler (pc_t *pc, _Jv_InterpMethod *meth, + java::lang::Throwable *ex) +{ +#ifdef DIRECT_THREADED + void *logical_pc = (void *) ((insn_slot *) (*pc) - 1); +#else + int logical_pc = (*pc) - 1 - meth->bytecode (); +#endif + _Jv_InterpException *exc = meth->exceptions (); + jclass exc_class = ex->getClass (); + + for (int i = 0; i < meth->exc_count; i++) + { + if (PCVAL (exc[i].start_pc) <= logical_pc + && logical_pc < PCVAL (exc[i].end_pc)) + { +#ifdef DIRECT_THREADED + jclass handler = (jclass) exc[i].handler_type.p; +#else + jclass handler = NULL; + if (exc[i].handler_type.i != 0) + handler + = (_Jv_Linker::resolve_pool_entry (meth->defining_class, + ex$ +#endif /* DIRECT_THREADED */ + if (handler == NULL || handler->isAssignableFrom (exc_class)) + { +#ifdef DIRECT_THREADED + (*pc) = (insn_slot *) exc[i].handler_pc.p; +#else + (*pc) = meth->bytecode () + exc[i].handler_pc.i; +#endif /* DIRECT_THREADED */ + return true; + } + } + } + return false; +} + + void _Jv_InterpMethod::get_line_table (jlong& start, jlong& end, jintArray& line_numbers, @@ -1409,6 +1497,30 @@ _Jv_InterpMethod::get_line_table (jlong& start, jlong& end, #endif // !DIRECT_THREADED } +int +_Jv_InterpMethod::get_local_var_table (char **name, char **sig, + char **generic_sig, jlong *startloc, + jint *length, jint *slot, + int table_slot) +{ + if (local_var_table == NULL) + return -2; + if (table_slot >= local_var_table_len) + return -1; + else + { + *name = local_var_table[table_slot].name; + *sig = local_var_table[table_slot].descriptor; + *generic_sig = local_var_table[table_slot].descriptor; + + *startloc = static_cast<jlong> + (local_var_table[table_slot].bytecode_start_pc); + *length = static_cast<jint> (local_var_table[table_slot].length); + *slot = static_cast<jint> (local_var_table[table_slot].slot); + } + return local_var_table_len - table_slot -1; +} + pc_t _Jv_InterpMethod::install_break (jlong index) { @@ -1455,6 +1567,23 @@ _Jv_InterpMethod::set_insn (jlong index, pc_t insn) return &code[index]; } +bool +_Jv_InterpMethod::breakpoint_at (jlong index) +{ + pc_t insn = get_insn (index); + if (insn != NULL) + { +#ifdef DIRECT_THREADED + return (insn->insn == breakpoint_insn->insn); +#else + pc_t code = reinterpret_cast<pc_t> (bytecode ()); + return (code[index] == breakpoint_insn); +#endif + } + + return false; +} + void * _Jv_JNIMethod::ncode (jclass klass) { @@ -1523,9 +1652,11 @@ _Jv_JNIMethod::ncode (jclass klass) static void throw_class_format_error (jstring msg) { - throw (msg + jthrowable t = (msg ? new java::lang::ClassFormatError (msg) : new java::lang::ClassFormatError); + REPORT_EXCEPTION (t); + throw t; } static void @@ -1534,6 +1665,70 @@ throw_class_format_error (const char *msg) throw_class_format_error (JvNewStringLatin1 (msg)); } +/* This function finds the method and location where the exception EXC + is caught in the stack frame. On return, it sets CATCH_METHOD and + CATCH_LOCATION with the method and location where the catch will + occur. If the exception is not caught, these are set to 0. + + This function should only be used with the DEBUG interpreter. */ +static void +find_catch_location (::java::lang::Throwable *exc, jthread thread, + jmethodID *catch_method, jlong *catch_loc) +{ + *catch_method = 0; + *catch_loc = 0; + + _Jv_InterpFrame *frame + = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame); + while (frame != NULL) + { + pc_t pc = frame->get_pc (); + _Jv_InterpMethod *imeth + = reinterpret_cast<_Jv_InterpMethod *> (frame->self); + if (imeth->check_handler (&pc, imeth, exc)) + { + // This method handles the exception. + *catch_method = imeth->get_method (); + *catch_loc = imeth->insn_index (pc); + return; + } + + frame = frame->next_interp; + } +} + +/* This method handles JVMTI notifications of thrown exceptions. It + calls find_catch_location to figure out where the exception is + caught (if it is caught). + + Like find_catch_location, this should only be called with the + DEBUG interpreter. Since a few exceptions occur outside the + interpreter proper, it is important to not call this function + without checking JVMTI_REQUESTED_EVENT(Exception) first. */ +void +_Jv_ReportJVMTIExceptionThrow (jthrowable ex) +{ + jthread thread = ::java::lang::Thread::currentThread (); + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + jmethodID throw_meth = frame->self->get_method (); + jlocation throw_loc = -1; + if (frame->frame_type == frame_interpreter) + { + _Jv_InterpFrame * iframe + = reinterpret_cast<_Jv_InterpFrame *> (frame); + _Jv_InterpMethod *imeth + = reinterpret_cast<_Jv_InterpMethod *> (frame->self); + throw_loc = imeth->insn_index (iframe->get_pc ()); + } + + jlong catch_loc; + jmethodID catch_method; + find_catch_location (ex, thread, &catch_method, &catch_loc); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION, thread, + _Jv_GetCurrentJNIEnv (), throw_meth, throw_loc, + ex, catch_method, catch_loc); +} + void @@ -1690,7 +1885,12 @@ void _Jv_CompileMethod (_Jv_InterpMethod* method) { if (method->prepared == NULL) - _Jv_InterpMethod::run (NULL, NULL, method); + { + if (JVMTI::enabled) + _Jv_InterpMethod::run_debug (NULL, NULL, method); + else + _Jv_InterpMethod::run (NULL, NULL, method); + } } #endif // DIRECT_THREADED diff --git a/libjava/java/lang/Class.h b/libjava/java/lang/Class.h index 615c81e4d9c..a1795f7e58c 100644 --- a/libjava/java/lang/Class.h +++ b/libjava/java/lang/Class.h @@ -1,6 +1,6 @@ // Class.h - Header file for java.lang.Class. -*- c++ -*- -/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -269,8 +269,11 @@ _Jv_Utf8Const *_Jv_GetClassNameUtf8 (jclass); // Finds a desired interpreter method in the given class or NULL if not found class _Jv_MethodBase; _Jv_MethodBase *_Jv_FindInterpreterMethod (jclass, jmethodID); +jstring _Jv_GetInterpClassSourceFile (jclass); #endif +jbyte _Jv_GetClassState (jclass); + // Friend classes and functions to implement the ClassLoader class java::lang::ClassLoader; class java::lang::VMClassLoader; @@ -571,7 +574,9 @@ private: #ifdef INTERPRETER friend _Jv_MethodBase *(::_Jv_FindInterpreterMethod) (jclass klass, jmethodID desired_method); + friend jstring ::_Jv_GetInterpClassSourceFile (jclass); #endif + friend jbyte (::_Jv_GetClassState) (jclass klass); // Friends classes and functions to implement the ClassLoader friend class java::lang::ClassLoader; diff --git a/libjava/java/lang/Thread.java b/libjava/java/lang/Thread.java index 02180711fc2..1e1e860cea0 100644 --- a/libjava/java/lang/Thread.java +++ b/libjava/java/lang/Thread.java @@ -185,6 +185,9 @@ public class Thread implements Runnable // This describes the top-most interpreter frame for this thread. RawData interp_frame; + + // This describes the top most frame in the composite (interp + JNI) stack + RawData frame; // Current state. volatile int state; diff --git a/libjava/java/lang/natClass.cc b/libjava/java/lang/natClass.cc index f96287d4fee..7bad53d6203 100644 --- a/libjava/java/lang/natClass.cc +++ b/libjava/java/lang/natClass.cc @@ -1,6 +1,6 @@ // natClass.cc - Implementation of java.lang.Class native methods. -/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -2079,3 +2079,21 @@ _Jv_GetMethodDeclaringClass (jmethodID method) return reinterpret_cast<jclass> (_Jv_StackTrace::ncodeMap->get (obj)); } +jbyte +_Jv_GetClassState (jclass klass) +{ + return klass->state; +} + +jstring +_Jv_GetInterpClassSourceFile (jclass klass) +{ + if (_Jv_IsInterpretedClass (klass)) + { + _Jv_InterpClass *iclass = + reinterpret_cast<_Jv_InterpClass *> (klass->aux_info); + return iclass->source_file_name; + } + + return NULL; +} diff --git a/libjava/java/lang/natThread.cc b/libjava/java/lang/natThread.cc index 564ed90d55a..42f18c4eb3a 100644 --- a/libjava/java/lang/natThread.cc +++ b/libjava/java/lang/natThread.cc @@ -27,6 +27,8 @@ details. */ #include <java/lang/NullPointerException.h> #include <jni.h> +#include <jvmti.h> +#include "jvmti-int.h" #ifdef ENABLE_JVMPI #include <jvmpi.h> @@ -215,6 +217,9 @@ java::lang::Thread::finish_ () nt->park_helper.deactivate (); group->removeThread (this); + if (JVMTI_REQUESTED_EVENT (ThreadEnd)) + _Jv_JVMTI_PostEvent (JVMTI_EVENT_THREAD_END, this, nt->jni_env); + #ifdef ENABLE_JVMPI if (_Jv_JVMPI_Notify_THREAD_END) { @@ -253,6 +258,12 @@ java::lang::Thread::finish_ () static void _Jv_NotifyThreadStart (java::lang::Thread* thread) { + if (JVMTI_REQUESTED_EVENT (ThreadStart)) + { + natThread *nt = reinterpret_cast<natThread *> (thread->data); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_THREAD_START, thread, nt->jni_env); + } + #ifdef ENABLE_JVMPI if (_Jv_JVMPI_Notify_THREAD_START) { diff --git a/libjava/jni.cc b/libjava/jni.cc index 4bd56c9b0e8..efe88991b9d 100644 --- a/libjava/jni.cc +++ b/libjava/jni.cc @@ -1,6 +1,6 @@ // jni.cc - JNI implementation, including the jump table. -/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -23,6 +23,7 @@ details. */ #include <jvmpi.h> #endif #include <jvmti.h> +#include "jvmti-int.h" #include <java/lang/Class.h> #include <java/lang/ClassLoader.h> @@ -456,6 +457,8 @@ _Jv_JNI_PopSystemFrame (JNIEnv *env) { jthrowable t = env->ex; env->ex = NULL; + if (JVMTI_REQUESTED_EVENT (Exception)) + _Jv_ReportJVMTIExceptionThrow (t); throw t; } } diff --git a/libjava/jvmti.cc b/libjava/jvmti.cc index b9646b7d101..d9bcc806074 100644 --- a/libjava/jvmti.cc +++ b/libjava/jvmti.cc @@ -1,6 +1,6 @@ // jvmti.cc - JVMTI implementation -/* Copyright (C) 2006 Free Software Foundation +/* Copyright (C) 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -27,16 +27,18 @@ details. */ #include <java/lang/Class.h> #include <java/lang/ClassLoader.h> -#include <java/lang/Object.h> #include <java/lang/OutOfMemoryError.h> #include <java/lang/Thread.h> #include <java/lang/ThreadGroup.h> +#include <java/lang/Thread$State.h> #include <java/lang/Throwable.h> #include <java/lang/VMClassLoader.h> #include <java/lang/reflect/Field.h> #include <java/lang/reflect/Modifier.h> #include <java/util/Collection.h> #include <java/util/HashMap.h> +#include <java/util/concurrent/locks/Lock.h> +#include <java/util/concurrent/locks/ReentrantReadWriteLock.h> #include <java/net/URL.h> static void check_enabled_events (void); @@ -44,6 +46,10 @@ static void check_enabled_event (jvmtiEvent); namespace JVMTI { + // Is JVMTI enabled? (i.e., any jvmtiEnv created?) + bool enabled; + + // Event notifications bool VMInit = false; bool VMDeath = false; bool ThreadStart = false; @@ -99,7 +105,8 @@ struct jvmti_env_list struct jvmti_env_list *next; }; static struct jvmti_env_list *_jvmtiEnvironments = NULL; -static java::lang::Object *_envListLock = NULL; +static java::util::concurrent::locks:: +ReentrantReadWriteLock *_envListLock = NULL; #define FOREACH_ENVIRONMENT(Ele) \ for (Ele = _jvmtiEnvironments; Ele != NULL; Ele = Ele->next) @@ -149,18 +156,28 @@ static java::lang::Object *_envListLock = NULL; } \ while (0) +#define CHECK_FOR_NATIVE_METHOD(AjmethodID) \ + do \ + { \ + jboolean is_native; \ + jvmtiError jerr = env->IsMethodNative (AjmethodID, &is_native); \ + if (jerr != JVMTI_ERROR_NONE) \ + return jerr; \ + if (is_native) \ + return JVMTI_ERROR_NATIVE_METHOD; \ + } \ + while (0) + static jvmtiError JNICALL _Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) { using namespace java::lang; THREAD_DEFAULT_TO_CURRENT (thread); - - Thread *t = reinterpret_cast<Thread *> (thread); - THREAD_CHECK_VALID (t); - THREAD_CHECK_IS_ALIVE (t); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); - _Jv_Thread_t *data = _Jv_ThreadGetData (t); + _Jv_Thread_t *data = _Jv_ThreadGetData (thread); _Jv_SuspendThread (data); return JVMTI_ERROR_NONE; } @@ -171,12 +188,10 @@ _Jv_JVMTI_ResumeThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) using namespace java::lang; THREAD_DEFAULT_TO_CURRENT (thread); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); - Thread *t = reinterpret_cast<Thread *> (thread); - THREAD_CHECK_VALID (t); - THREAD_CHECK_IS_ALIVE (t); - - _Jv_Thread_t *data = _Jv_ThreadGetData (t); + _Jv_Thread_t *data = _Jv_ThreadGetData (thread); _Jv_ResumeThread (data); return JVMTI_ERROR_NONE; } @@ -191,14 +206,260 @@ _Jv_JVMTI_InterruptThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread) if (thread == NULL) return JVMTI_ERROR_INVALID_THREAD; - Thread *real_thread = reinterpret_cast<Thread *> (thread); - THREAD_CHECK_VALID (real_thread); - THREAD_CHECK_IS_ALIVE (real_thread); - real_thread->interrupt(); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); + thread->interrupt(); + return JVMTI_ERROR_NONE; +} + +// This method performs the common tasks to get and set variables of all types. +// It is called by the _Jv_JVMTI_Get/SetLocalInt/Object/.... methods. +static jvmtiError +getLocalFrame (jvmtiEnv *env, jthread thread, jint depth, jint slot, char type, + _Jv_InterpFrame **iframe) +{ + using namespace java::lang; + + REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); + + ILLEGAL_ARGUMENT (depth < 0); + + THREAD_DEFAULT_TO_CURRENT (thread); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); + + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + + for (int i = 0; i < depth; i++) + { + frame = frame->next; + + if (frame == NULL) + return JVMTI_ERROR_NO_MORE_FRAMES; + } + + if (frame->frame_type == frame_native) + return JVMTI_ERROR_OPAQUE_FRAME; + + jint max_locals; + jvmtiError jerr = env->GetMaxLocals (reinterpret_cast<jmethodID> + (frame->self->get_method ()), + &max_locals); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + _Jv_InterpFrame *tmp_iframe = reinterpret_cast<_Jv_InterpFrame *> (frame); + + // The second slot taken up by a long type is marked as type 'x' meaning it + // is not valid for access since it holds only the 4 low bytes of the value. + if (tmp_iframe->locals_type[slot] == 'x') + return JVMTI_ERROR_INVALID_SLOT; + + if (tmp_iframe->locals_type[slot] != type) + return JVMTI_ERROR_TYPE_MISMATCH; + + // Check for invalid slots, if the type is a long type, we must check that + // the next slot is valid as well. + if (slot < 0 || slot >= max_locals + || ((type == 'l' || type == 'd') && slot + 1 >= max_locals)) + return JVMTI_ERROR_INVALID_SLOT; + + *iframe = tmp_iframe; + return JVMTI_ERROR_NONE; } -jvmtiError +static jvmtiError JNICALL +_Jv_JVMTI_GetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jobject *value) +{ + NULL_CHECK (value); + + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + *value = frame->locals[slot].o; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_SetLocalObject (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jobject value) +{ + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'o', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + frame->locals[slot].o = value; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_GetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jint *value) +{ + NULL_CHECK (value); + + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + *value = frame->locals[slot].i; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_SetLocalInt (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jint value) +{ + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'i', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + frame->locals[slot].i = value; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_GetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jlong *value) +{ + NULL_CHECK (value); + + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + +#if SIZEOF_VOID_P==8 + *value = frame->locals[slot].l; +#else + _Jv_word2 val; + val.ia[0] = frame->locals[slot].ia[0]; + val.ia[1] = frame->locals[slot + 1].ia[0]; + *value = val.l; +#endif + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_SetLocalLong (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jlong value) +{ + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'l', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + +#if SIZEOF_VOID_P==8 + frame->locals[slot].l = value; +#else + _Jv_word2 val; + val.l = value; + frame->locals[slot].ia[0] = val.ia[0]; + frame->locals[slot + 1].ia[0] = val.ia[1]; +#endif + + return JVMTI_ERROR_NONE; +} + + +static jvmtiError JNICALL +_Jv_JVMTI_GetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jfloat *value) +{ + NULL_CHECK (value); + + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + *value = frame->locals[slot].f; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_SetLocalFloat (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jfloat value) +{ + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'f', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + frame->locals[slot].f = value; + + return JVMTI_ERROR_NONE; +} + + +static jvmtiError JNICALL +_Jv_JVMTI_GetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jdouble *value) +{ + NULL_CHECK (value); + + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + +#if SIZEOF_VOID_P==8 + *value = frame->locals[slot].d; +#else + _Jv_word2 val; + val.ia[0] = frame->locals[slot].ia[0]; + val.ia[1] = frame->locals[slot + 1].ia[0]; + *value = val.d; +#endif + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_SetLocalDouble (jvmtiEnv *env, jthread thread, jint depth, jint slot, + jdouble value) +{ + _Jv_InterpFrame *frame; + jvmtiError jerr = getLocalFrame (env, thread, depth, slot, 'd', &frame); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + +#if SIZEOF_VOID_P==8 + frame->locals[slot].d = value; +#else + _Jv_word2 val; + val.d = value; + frame->locals[slot].ia[0] = val.ia[0]; + frame->locals[slot + 1].ia[0] = val.ia[1]; +#endif + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt, jthread **threads) { @@ -207,7 +468,6 @@ _Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt, NULL_CHECK (threads); using namespace java::lang; - Thread *thr = Thread::currentThread (); ThreadGroup *root_grp = ThreadGroup::root; jint estimate = root_grp->activeCount (); @@ -217,10 +477,9 @@ _Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt, // Allocate some extra space since threads can be created between calls try { - thr_arr - = reinterpret_cast<JArray<Thread *> *> (JvNewObjectArray - ((estimate * 2), - &Thread::class$, NULL)); + thr_arr = reinterpret_cast<JArray<Thread *> *> (JvNewObjectArray + ((estimate * 2), + &Thread::class$, NULL)); } catch (java::lang::OutOfMemoryError *err) { @@ -244,6 +503,85 @@ _Jv_JVMTI_GetAllThreads(MAYBE_UNUSED jvmtiEnv *env, jint *thread_cnt, } static jvmtiError JNICALL +_Jv_JVMTI_GetFrameCount (MAYBE_UNUSED jvmtiEnv *env, jthread thread, + jint *frame_count) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); + + NULL_CHECK (frame_count); + + using namespace java::lang; + + THREAD_DEFAULT_TO_CURRENT (thread); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); + + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + (*frame_count) = frame->depth (); + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_GetThreadState (MAYBE_UNUSED jvmtiEnv *env, jthread thread, + jint *thread_state_ptr) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); + + THREAD_DEFAULT_TO_CURRENT (thread); + THREAD_CHECK_VALID (thread); + NULL_CHECK (thread_state_ptr); + + jint state = 0; + if (thread->isAlive ()) + { + state |= JVMTI_THREAD_STATE_ALIVE; + + _Jv_Thread_t *data = _Jv_ThreadGetData (thread); + if (_Jv_IsThreadSuspended (data)) + state |= JVMTI_THREAD_STATE_SUSPENDED; + + if (thread->isInterrupted ()) + state |= JVMTI_THREAD_STATE_INTERRUPTED; + + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + if (frame != NULL && frame->frame_type == frame_native) + state |= JVMTI_THREAD_STATE_IN_NATIVE; + + using namespace java::lang; + Thread$State *ts = thread->getState (); + if (ts == Thread$State::RUNNABLE) + state |= JVMTI_THREAD_STATE_RUNNABLE; + else if (ts == Thread$State::BLOCKED) + state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; + else if (ts == Thread$State::TIMED_WAITING + || ts == Thread$State::WAITING) + { + state |= JVMTI_THREAD_STATE_WAITING; + state |= ((ts == Thread$State::WAITING) + ? JVMTI_THREAD_STATE_WAITING_INDEFINITELY + : JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT); + + /* FIXME: We don't have a way to tell + the caller why the thread is suspended, + i.e., JVMTI_THREAD_STATE_SLEEPING, + JVMTI_THREAD_STATE_PARKED, and + JVMTI_THREAD_STATE_IN_OBJECT_WAIT + are never set. */ + } + } + else + { + using namespace java::lang; + Thread$State *ts = thread->getState (); + if (ts == Thread$State::TERMINATED) + state |= JVMTI_THREAD_STATE_TERMINATED; + } + + *thread_state_ptr = state; + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_CreateRawMonitor (MAYBE_UNUSED jvmtiEnv *env, const char *name, jrawMonitorID *result) { @@ -422,6 +760,36 @@ _Jv_JVMTI_Deallocate (MAYBE_UNUSED jvmtiEnv *env, unsigned char *mem) } static jvmtiError JNICALL +_Jv_JVMTI_GetClassStatus (MAYBE_UNUSED jvmtiEnv *env, jclass klass, + jint *status_ptr) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); + NULL_CHECK (status_ptr); + if (klass == NULL) + return JVMTI_ERROR_INVALID_CLASS; + + if (klass->isArray ()) + *status_ptr = JVMTI_CLASS_STATUS_ARRAY; + else if (klass->isPrimitive ()) + *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE; + else + { + jbyte state = _Jv_GetClassState (klass); + *status_ptr = 0; + if (state >= JV_STATE_LINKED) + (*status_ptr) |= JVMTI_CLASS_STATUS_VERIFIED; + if (state >= JV_STATE_PREPARED) + (*status_ptr) |= JVMTI_CLASS_STATUS_PREPARED; + if (state == JV_STATE_ERROR || state == JV_STATE_PHANTOM) + (*status_ptr) |= JVMTI_CLASS_STATUS_ERROR; + else if (state == JV_STATE_DONE) + (*status_ptr) |= JVMTI_CLASS_STATUS_INITIALIZED; + } + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_GetClassModifiers (MAYBE_UNUSED jvmtiEnv *env, jclass klass, jint *mods) { @@ -540,6 +908,48 @@ _Jv_JVMTI_IsFieldSynthetic (MAYBE_UNUSED jvmtiEnv *env, jclass klass, } static jvmtiError JNICALL +_Jv_JVMTI_GetMethodName (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, + char **name_ptr, char **signature_ptr, + char **generic_ptr) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); + + if (method == NULL) + return JVMTI_ERROR_INVALID_METHODID; + + if (name_ptr != NULL) + { + int len = static_cast<int> (method->name->len ()); + *name_ptr = (char *) _Jv_MallocUnchecked (len + 1); + if (*name_ptr == NULL) + return JVMTI_ERROR_OUT_OF_MEMORY; + strncpy (*name_ptr, method->name->chars (), len); + (*name_ptr)[len] = '\0'; + } + + if (signature_ptr != NULL) + { + int len = static_cast<int> (method->signature->len ()); + *signature_ptr = (char *) _Jv_MallocUnchecked (len + 1); + if (*signature_ptr == NULL) + { + if (name_ptr != NULL) + _Jv_Free (*name_ptr); + return JVMTI_ERROR_OUT_OF_MEMORY; + } + strncpy (*signature_ptr, method->signature->chars (), len); + (*signature_ptr)[len] = '\0'; + } + + if (generic_ptr != NULL) + { + *generic_ptr = NULL; + } + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_GetMethodModifiers (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, jint *result) { @@ -602,6 +1012,88 @@ _Jv_JVMTI_GetLineNumberTable (jvmtiEnv *env, jmethodID method, } static jvmtiError JNICALL +_Jv_JVMTI_GetLocalVariableTable (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, + jint *num_locals, + jvmtiLocalVariableEntry **locals) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); + NULL_CHECK (num_locals); + NULL_CHECK (locals); + + CHECK_FOR_NATIVE_METHOD(method); + + jclass klass; + jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> + (_Jv_FindInterpreterMethod (klass, method)); + + if (imeth == NULL) + return JVMTI_ERROR_INVALID_METHODID; + + jerr = env->GetMaxLocals (method, num_locals); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + jerr = env->Allocate (static_cast<jlong> + ((*num_locals) * sizeof (jvmtiLocalVariableEntry)), + reinterpret_cast<unsigned char **> (locals)); + + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + //the slot in the methods local_var_table to get + int table_slot = 0; + char *name; + char *sig; + char *generic_sig; + + while (table_slot < *num_locals + && imeth->get_local_var_table (&name, &sig, &generic_sig, + &((((*locals)[table_slot].start_location))), + &((*locals)[table_slot].length), + &((*locals)[table_slot].slot), + table_slot) + >= 0) + { + char **str_ptr = &(*locals)[table_slot].name; + jerr = env->Allocate (static_cast<jlong> (strlen (name) + 1), + reinterpret_cast<unsigned char **> (str_ptr)); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + strcpy ((*locals)[table_slot].name, name); + + str_ptr = &(*locals)[table_slot].signature; + jerr = env->Allocate (static_cast<jlong> (strlen (sig) + 1), + reinterpret_cast<unsigned char **> (str_ptr)); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + strcpy ((*locals)[table_slot].signature, sig); + + str_ptr = &(*locals)[table_slot].generic_signature; + jerr = env->Allocate (static_cast<jlong> (strlen (generic_sig) + 1), + reinterpret_cast<unsigned char **> (str_ptr)); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + strcpy ((*locals)[table_slot].generic_signature, generic_sig); + + table_slot++; + } + + if (table_slot == 0) + return JVMTI_ERROR_ABSENT_INFORMATION; + + // If there are double or long variables in the table, the the table will be + // smaller than the max number of slots, so correct for this here. + if ((table_slot) < *num_locals) + *num_locals = table_slot; + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_IsMethodNative (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, jboolean *result) { @@ -631,6 +1123,75 @@ _Jv_JVMTI_IsMethodSynthetic (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, } static jvmtiError JNICALL +_Jv_JVMTI_GetMaxLocals (jvmtiEnv *env, jmethodID method, jint *max_locals) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); + NULL_CHECK (max_locals); + + CHECK_FOR_NATIVE_METHOD (method); + + jclass klass; + jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> + (_Jv_FindInterpreterMethod (klass, method)); + + if (imeth == NULL) + return JVMTI_ERROR_INVALID_METHODID; + + *max_locals = imeth->get_max_locals (); + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL +_Jv_JVMTI_GetArgumentsSize (jvmtiEnv *env, jmethodID method, jint *size) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE); + NULL_CHECK (size); + + CHECK_FOR_NATIVE_METHOD (method); + + jvmtiError jerr; + char *sig; + jint num_slots = 0; + + jerr = env->GetMethodName (method, NULL, &sig, NULL); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + // If the method is non-static add a slot for the "this" pointer. + if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0) + num_slots++; + + for (int i = 0; sig[i] != ')'; i++) + { + if (sig[i] == 'Z' || sig[i] == 'B' || sig[i] == 'C' || sig[i] == 'S' + || sig[i] == 'I' || sig[i] == 'F') + num_slots++; + else if (sig[i] == 'J' || sig[i] == 'D') + { + // If this is an array of wide types it uses a single slot + if (i > 0 && sig[i - 1] == '[') + num_slots++; + else + num_slots += 2; + } + else if (sig[i] == 'L') + { + num_slots++; + while (sig[i] != ';') + i++; + } + } + + *size = num_slots; + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_GetMethodDeclaringClass (MAYBE_UNUSED jvmtiEnv *env, jmethodID method, jclass *declaring_class_ptr) @@ -683,6 +1244,80 @@ _Jv_JVMTI_GetClassLoaderClasses (MAYBE_UNUSED jvmtiEnv *env, } static jvmtiError JNICALL +_Jv_JVMTI_GetStackTrace (MAYBE_UNUSED jvmtiEnv *env, jthread thread, + jint start_depth, jint max_frames, + jvmtiFrameInfo *frames, jint *frame_count) +{ + REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); + + ILLEGAL_ARGUMENT (max_frames < 0); + + NULL_CHECK (frames); + NULL_CHECK (frame_count); + + using namespace java::lang; + + THREAD_DEFAULT_TO_CURRENT (thread); + THREAD_CHECK_VALID (thread); + THREAD_CHECK_IS_ALIVE (thread); + + jvmtiError jerr = env->GetFrameCount (thread, frame_count); + if (jerr != JVMTI_ERROR_NONE) + return jerr; + + // start_depth can be either a positive number, indicating the depth of the + // stack at which to begin the trace, or a negative number indicating the + // number of frames at the bottom of the stack to exclude. These checks + // ensure that it is a valid value in either case + + ILLEGAL_ARGUMENT (start_depth >= (*frame_count)); + ILLEGAL_ARGUMENT (start_depth < (-(*frame_count))); + + _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame); + + // If start_depth is negative use this to determine at what depth to start + // the trace by adding it to the length of the call stack. This allows the + // use of the same frame "discarding" mechanism as for a positive start_depth + if (start_depth < 0) + start_depth = *frame_count + start_depth; + + // If start_depth > 0 "remove" start_depth frames from the beginning + // of the stack before beginning the trace by moving along the frame list. + while (start_depth > 0) + { + frame = frame->next; + start_depth--; + (*frame_count)--; + } + + // Now check to see if the array supplied by the agent is large enough to + // hold frame_count frames, after adjustment for start_depth. + if ((*frame_count) > max_frames) + (*frame_count) = max_frames; + + for (int i = 0; i < (*frame_count); i++) + { + frames[i].method = frame->self->get_method (); + + // Set the location in the frame, native frames have location = -1 + if (frame->frame_type == frame_interpreter) + { + _Jv_InterpMethod *imeth + = static_cast<_Jv_InterpMethod *> (frame->self); + _Jv_InterpFrame *interp_frame + = static_cast<_Jv_InterpFrame *> (frame); + frames[i].location = imeth->insn_index (interp_frame->get_pc ()); + } + else + frames[i].location = -1; + + frame = frame->next; + } + + return JVMTI_ERROR_NONE; +} + +static jvmtiError JNICALL _Jv_JVMTI_ForceGarbageCollection (MAYBE_UNUSED jvmtiEnv *env) { REQUIRE_PHASE (env, JVMTI_PHASE_LIVE); @@ -723,7 +1358,7 @@ _Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env) return JVMTI_ERROR_INVALID_ENVIRONMENT; else { - JvSynchronize dummy (_envListLock); + _envListLock->writeLock ()->lock (); if (_jvmtiEnvironments->env == env) { struct jvmti_env_list *next = _jvmtiEnvironments->next; @@ -736,12 +1371,16 @@ _Jv_JVMTI_DisposeEnvironment (jvmtiEnv *env) while (e->next != NULL && e->next->env != env) e = e->next; if (e->next == NULL) - return JVMTI_ERROR_INVALID_ENVIRONMENT; + { + _envListLock->writeLock ()->unlock (); + return JVMTI_ERROR_INVALID_ENVIRONMENT; + } struct jvmti_env_list *next = e->next->next; _Jv_Free (e->next); e->next = next; } + _envListLock->writeLock ()->unlock (); } _Jv_Free (env); @@ -1042,18 +1681,24 @@ check_enabled_event (jvmtiEvent type) int index = EVENT_INDEX (type); // safe since caller checks this - JvSynchronize dummy (_envListLock); - struct jvmti_env_list *e; - FOREACH_ENVIRONMENT (e) + if (_jvmtiEnvironments != NULL) { - char *addr - = reinterpret_cast<char *> (&e->env->callbacks) + offset; - void **callback = reinterpret_cast<void **> (addr); - if (e->env->enabled[index] && *callback != NULL) + _envListLock->readLock ()->lock (); + struct jvmti_env_list *e; + FOREACH_ENVIRONMENT (e) { - *enabled = true; - return; + char *addr + = reinterpret_cast<char *> (&e->env->callbacks) + offset; + void **callback = reinterpret_cast<void **> (addr); + if (e->env->enabled[index] && *callback != NULL) + { + *enabled = true; + _envListLock->readLock ()->unlock (); + return; + } } + + _envListLock->readLock ()->unlock (); } *enabled = false; @@ -1102,10 +1747,8 @@ _Jv_JVMTI_SetEventNotificationMode (jvmtiEnv *env, jvmtiEventMode mode, if (event_thread != NULL) { - using namespace java::lang; - Thread *t = reinterpret_cast<Thread *> (event_thread); - THREAD_CHECK_VALID (t); - THREAD_CHECK_IS_ALIVE (t); + THREAD_CHECK_VALID (event_thread); + THREAD_CHECK_IS_ALIVE (event_thread); } bool enabled; @@ -1421,21 +2064,21 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetTopThreadGroups UNIMPLEMENTED, // GetThreadGroupInfo UNIMPLEMENTED, // GetThreadGroupChildren - UNIMPLEMENTED, // GetFrameCount - UNIMPLEMENTED, // GetThreadState + _Jv_JVMTI_GetFrameCount, // GetFrameCount + _Jv_JVMTI_GetThreadState, // GetThreadState RESERVED, // reserved18 UNIMPLEMENTED, // GetFrameLocation UNIMPLEMENTED, // NotifyPopFrame - UNIMPLEMENTED, // GetLocalObject - UNIMPLEMENTED, // GetLocalInt - UNIMPLEMENTED, // GetLocalLong - UNIMPLEMENTED, // GetLocalFloat - UNIMPLEMENTED, // GetLocalDouble - UNIMPLEMENTED, // SetLocalObject - UNIMPLEMENTED, // SetLocalInt - UNIMPLEMENTED, // SetLocalLong - UNIMPLEMENTED, // SetLocalFloat - UNIMPLEMENTED, // SetLocalDouble + _Jv_JVMTI_GetLocalObject, // GetLocalObject + _Jv_JVMTI_GetLocalInt, // GetLocalInt + _Jv_JVMTI_GetLocalLong, // GetLocalLong + _Jv_JVMTI_GetLocalFloat, // GetLocalFloat + _Jv_JVMTI_GetLocalDouble, // GetLocalDouble + _Jv_JVMTI_SetLocalObject, // SetLocalObject + _Jv_JVMTI_SetLocalInt, // SetLocalInt + _Jv_JVMTI_SetLocalLong, // SetLocalLong + _Jv_JVMTI_SetLocalFloat, // SetLocalFloat + _Jv_JVMTI_SetLocalDouble, // SetLocalDouble _Jv_JVMTI_CreateRawMonitor, // CreateRawMonitor _Jv_JVMTI_DestroyRawMonitor, // DestroyRawMonitor _Jv_JVMTI_RawMonitorEnter, // RawMonitorEnter @@ -1454,7 +2097,7 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = _Jv_JVMTI_Allocate, // Allocate _Jv_JVMTI_Deallocate, // Deallocate UNIMPLEMENTED, // GetClassSignature - UNIMPLEMENTED, // GetClassStatus + _Jv_JVMTI_GetClassStatus, // GetClassStatus UNIMPLEMENTED, // GetSourceFileName _Jv_JVMTI_GetClassModifiers, // GetClassModifiers _Jv_JVMTI_GetClassMethods, // GetClassMethods @@ -1469,15 +2112,15 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetFieldDeclaringClass _Jv_JVMTI_GetFieldModifiers, // GetFieldModifiers _Jv_JVMTI_IsFieldSynthetic, // IsFieldSynthetic - UNIMPLEMENTED, // GetMethodName + _Jv_JVMTI_GetMethodName, // GetMethodName _Jv_JVMTI_GetMethodDeclaringClass, // GetMethodDeclaringClass _Jv_JVMTI_GetMethodModifiers, // GetMethodModifers RESERVED, // reserved67 - UNIMPLEMENTED, // GetMaxLocals - UNIMPLEMENTED, // GetArgumentsSize + _Jv_JVMTI_GetMaxLocals, // GetMaxLocals + _Jv_JVMTI_GetArgumentsSize, // GetArgumentsSize _Jv_JVMTI_GetLineNumberTable, // GetLineNumberTable UNIMPLEMENTED, // GetMethodLocation - UNIMPLEMENTED, // GetLocalVariableTable + _Jv_JVMTI_GetLocalVariableTable, // GetLocalVariableTable RESERVED, // reserved73 RESERVED, // reserved74 UNIMPLEMENTED, // GetBytecodes @@ -1509,7 +2152,7 @@ struct _Jv_jvmtiEnv _Jv_JVMTI_Interface = UNIMPLEMENTED, // GetThreadListStackTraces UNIMPLEMENTED, // GetThreadLocalStorage UNIMPLEMENTED, // SetThreadLocalStorage - UNIMPLEMENTED, // GetStackTrace + _Jv_JVMTI_GetStackTrace, // GetStackTrace RESERVED, // reserved105 UNIMPLEMENTED, // GetTag UNIMPLEMENTED, // SetTag @@ -1568,25 +2211,27 @@ _Jv_GetJVMTIEnv (void) _Jv_JVMTIEnv *env = (_Jv_JVMTIEnv *) _Jv_MallocUnchecked (sizeof (_Jv_JVMTIEnv)); env->p = &_Jv_JVMTI_Interface; + struct jvmti_env_list *element + = (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list)); + element->env = env; + element->next = NULL; - { - JvSynchronize dummy (_envListLock); - struct jvmti_env_list *element - = (struct jvmti_env_list *) _Jv_MallocUnchecked (sizeof (struct jvmti_env_list)); - element->env = env; - element->next = NULL; - - if (_jvmtiEnvironments == NULL) - _jvmtiEnvironments = element; - else - { - struct jvmti_env_list *e; - for (e = _jvmtiEnvironments; e->next != NULL; e = e->next) - ; - e->next = element; - } - } + _envListLock->writeLock ()->lock (); + if (_jvmtiEnvironments == NULL) + _jvmtiEnvironments = element; + else + { + struct jvmti_env_list *e; + for (e = _jvmtiEnvironments; e->next != NULL; e = e->next) + ; + e->next = element; + } + _envListLock->writeLock ()->unlock (); + /* Mark JVMTI active. This is used to force the interpreter + to use either debugging or non-debugging code. Once JVMTI + has been enabled, the non-debug interpreter cannot be used. */ + JVMTI::enabled = true; return env; } @@ -1594,7 +2239,8 @@ void _Jv_JVMTI_Init () { _jvmtiEnvironments = NULL; - _envListLock = new java::lang::Object (); + _envListLock + = new java::util::concurrent::locks::ReentrantReadWriteLock (); // No environments, so this should set all JVMTI:: members to false check_enabled_events (); @@ -1958,7 +2604,7 @@ _Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...) va_list args; va_start (args, event_thread); - JvSynchronize dummy (_envListLock); + _envListLock->readLock ()->lock (); struct jvmti_env_list *e; FOREACH_ENVIRONMENT (e) { @@ -1974,6 +2620,6 @@ _Jv_JVMTI_PostEvent (jvmtiEvent type, jthread event_thread, ...) post_event (e->env, type, event_thread, args); } } - + _envListLock->readLock ()->unlock (); va_end (args); } diff --git a/libjava/link.cc b/libjava/link.cc index cfcbaf9e31a..1507b01310b 100644 --- a/libjava/link.cc +++ b/libjava/link.cc @@ -33,6 +33,8 @@ details. */ #include <limits.h> #include <java-cpool.h> #include <execution.h> +#include <jvmti.h> +#include "jvmti-int.h" #include <java/lang/Class.h> #include <java/lang/String.h> #include <java/lang/StringBuffer.h> @@ -1982,33 +1984,35 @@ _Jv_Linker::wait_for_state (jclass klass, int state) if (klass->state >= state) return; - JvSynchronize sync (klass); - - // This is similar to the strategy for class initialization. If we - // already hold the lock, just leave. java::lang::Thread *self = java::lang::Thread::currentThread(); - while (klass->state <= state - && klass->thread - && klass->thread != self) - klass->wait (); - java::lang::Thread *save = klass->thread; - klass->thread = self; + { + JvSynchronize sync (klass); - // Allocate memory for static fields and constants. - if (GC_base (klass) && klass->fields && ! GC_base (klass->fields)) - { - jsize count = klass->field_count; - if (count) - { - _Jv_Field* fields - = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field)); - memcpy ((void*)fields, - (void*)klass->fields, - count * sizeof (_Jv_Field)); - klass->fields = fields; - } - } + // This is similar to the strategy for class initialization. If we + // already hold the lock, just leave. + while (klass->state <= state + && klass->thread + && klass->thread != self) + klass->wait (); + + java::lang::Thread *save = klass->thread; + klass->thread = self; + + // Allocate memory for static fields and constants. + if (GC_base (klass) && klass->fields && ! GC_base (klass->fields)) + { + jsize count = klass->field_count; + if (count) + { + _Jv_Field* fields + = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field)); + memcpy ((void*)fields, + (void*)klass->fields, + count * sizeof (_Jv_Field)); + klass->fields = fields; + } + } // Print some debugging info if requested. Interpreted classes are // handled in defineclass, so we only need to handle the two @@ -2022,49 +2026,59 @@ _Jv_Linker::wait_for_state (jclass klass, int state) ++gcj::loadedClasses; } - try - { - if (state >= JV_STATE_LOADING && klass->state < JV_STATE_LOADING) - { - ensure_supers_installed (klass); - klass->set_state(JV_STATE_LOADING); - } + try + { + if (state >= JV_STATE_LOADING && klass->state < JV_STATE_LOADING) + { + ensure_supers_installed (klass); + klass->set_state(JV_STATE_LOADING); + } - if (state >= JV_STATE_LOADED && klass->state < JV_STATE_LOADED) - { - ensure_method_table_complete (klass); - klass->set_state(JV_STATE_LOADED); - } + if (state >= JV_STATE_LOADED && klass->state < JV_STATE_LOADED) + { + ensure_method_table_complete (klass); + klass->set_state(JV_STATE_LOADED); + } - if (state >= JV_STATE_PREPARED && klass->state < JV_STATE_PREPARED) - { - ensure_fields_laid_out (klass); - make_vtable (klass); - layout_interface_methods (klass); - prepare_constant_time_tables (klass); - klass->set_state(JV_STATE_PREPARED); - } + if (state >= JV_STATE_PREPARED && klass->state < JV_STATE_PREPARED) + { + ensure_fields_laid_out (klass); + make_vtable (klass); + layout_interface_methods (klass); + prepare_constant_time_tables (klass); + klass->set_state(JV_STATE_PREPARED); + } - if (state >= JV_STATE_LINKED && klass->state < JV_STATE_LINKED) - { - if (gcj::verifyClasses) - verify_class (klass); + if (state >= JV_STATE_LINKED && klass->state < JV_STATE_LINKED) + { + if (gcj::verifyClasses) + verify_class (klass); - ensure_class_linked (klass); - link_exception_table (klass); - link_symbol_table (klass); - klass->set_state(JV_STATE_LINKED); - } - } - catch (java::lang::Throwable *exc) - { - klass->thread = save; - klass->set_state(JV_STATE_ERROR); - throw exc; - } + ensure_class_linked (klass); + link_exception_table (klass); + link_symbol_table (klass); + klass->set_state(JV_STATE_LINKED); + } + } + catch (java::lang::Throwable *exc) + { + klass->thread = save; + klass->set_state(JV_STATE_ERROR); + throw exc; + } + + klass->thread = save; - klass->thread = save; + if (klass->state == JV_STATE_ERROR) + throw new java::lang::LinkageError; + } - if (klass->state == JV_STATE_ERROR) - throw new java::lang::LinkageError; + if (__builtin_expect (klass->state == JV_STATE_LINKED, false) + && state >= JV_STATE_LINKED + && JVMTI_REQUESTED_EVENT (ClassPrepare)) + { + JNIEnv *jni_env = _Jv_GetCurrentJNIEnv (); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_CLASS_PREPARE, self, jni_env, + klass); + } } diff --git a/libjava/nogc.cc b/libjava/nogc.cc index 126e4de5c2b..079422d0911 100644 --- a/libjava/nogc.cc +++ b/libjava/nogc.cc @@ -184,3 +184,9 @@ void _Jv_ResumeThread (_Jv_Thread_t *thread) { } + +int +_Jv_IsThreadSuspended (_Jv_Thread_t *thread) +{ + return 0; +} diff --git a/libjava/prims.cc b/libjava/prims.cc index 4f8df34a3ab..bc85bdc2b13 100644 --- a/libjava/prims.cc +++ b/libjava/prims.cc @@ -1,6 +1,6 @@ // prims.cc - Code for core of runtime environment. -/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -32,6 +32,9 @@ details. */ #include <java/lang/ThreadGroup.h> #endif +#include <jvmti.h> +#include "jvmti-int.h" + #ifndef DISABLE_GETENV_PROPERTIES #include <ctype.h> #include <java-props.h> @@ -66,8 +69,6 @@ details. */ #include <execution.h> #include <gnu/classpath/jdwp/Jdwp.h> #include <gnu/classpath/jdwp/VMVirtualMachine.h> -#include <gnu/classpath/jdwp/event/VmDeathEvent.h> -#include <gnu/classpath/jdwp/event/VmInitEvent.h> #include <gnu/java/lang/MainThread.h> #ifdef USE_LTDL @@ -107,6 +108,16 @@ static bool remoteDebug = false; static char defaultJdwpOptions[] = ""; static char *jdwpOptions = defaultJdwpOptions; +// Typedefs for JVMTI agent functions. +typedef jint jvmti_agent_onload_func (JavaVM *vm, char *options, + void *reserved); +typedef jint jvmti_agent_onunload_func (JavaVM *vm); + +// JVMTI agent function pointers. +static jvmti_agent_onload_func *jvmti_agentonload = NULL; +static jvmti_agent_onunload_func *jvmti_agentonunload = NULL; +static char *jvmti_agent_opts; + // Argument support. int _Jv_GetNbArgs (void) @@ -1357,6 +1368,62 @@ parse_verbose_args (char* option_string, return 0; } +// This function loads the agent functions for JVMTI from the library indicated +// by name. It returns a negative value on failure, the value of which +// indicates where ltdl failed, it also prints an error message. +static jint +load_jvmti_agent (const char *name) +{ +#ifdef USE_LTDL + if (lt_dlinit ()) + { + fprintf (stderr, + "libgcj: Error in ltdl init while loading agent library.\n"); + return -1; + } + + lt_dlhandle lib = lt_dlopenext (name); + if (!lib) + { + fprintf (stderr, + "libgcj: Error opening agent library.\n"); + return -2; + } + + if (lib) + { + jvmti_agentonload + = (jvmti_agent_onload_func *) lt_dlsym (lib, "Agent_OnLoad"); + + if (!jvmti_agentonload) + { + fprintf (stderr, + "libgcj: Error finding agent function in library %s.\n", + name); + lt_dlclose (lib); + lib = NULL; + return -4; + } + else + { + jvmti_agentonunload + = (jvmti_agent_onunload_func *) lt_dlsym (lib, "Agent_OnUnload"); + + return 0; + } + } + else + { + fprintf (stderr, "libgcj: Library %s not found in library path.\n", name); + return -3; + } + +#endif /* USE_LTDL */ + + // If LTDL cannot be used, return an error code indicating this. + return -99; +} + static jint parse_init_args (JvVMInitArgs* vm_args) { @@ -1409,6 +1476,95 @@ parse_init_args (JvVMInitArgs* vm_args) continue; } + else if (! strncmp (option_string, "-agentlib", sizeof ("-agentlib") - 1)) + { + char *strPtr; + + if (strlen(option_string) > (sizeof ("-agentlib:") - 1)) + strPtr = &option_string[sizeof ("-agentlib:") - 1]; + else + { + fprintf (stderr, + "libgcj: Malformed agentlib argument %s: expected lib name\n", + option_string); + return -1; + } + + // These are optional arguments to pass to the agent library. + jvmti_agent_opts = strchr (strPtr, '='); + + if (! strncmp (strPtr, "jdwp", 4)) + { + // We want to run JDWP here so set the correct variables. + remoteDebug = true; + jdwpOptions = ++jvmti_agent_opts; + } + else + { + jint nameLength; + + if (jvmti_agent_opts == NULL) + nameLength = strlen (strPtr); + else + { + nameLength = jvmti_agent_opts - strPtr; + jvmti_agent_opts++; + } + + char lib_name[nameLength + 3 + 1]; + strcpy (lib_name, "lib"); + strncat (lib_name, strPtr, nameLength); + + jint result = load_jvmti_agent (lib_name); + + if (result < 0) + { + return -1; + } + } + + continue; + } + else if (! strncmp (option_string, "-agentpath:", + sizeof ("-agentpath:") - 1)) + { + char *strPtr; + + if (strlen(option_string) > 10) + strPtr = &option_string[10]; + else + { + fprintf (stderr, + "libgcj: Malformed agentlib argument %s: expected lib path\n", + option_string); + return -1; + } + + // These are optional arguments to pass to the agent library. + jvmti_agent_opts = strchr (strPtr, '='); + + jint nameLength; + + if (jvmti_agent_opts == NULL) + nameLength = strlen (strPtr); + else + { + nameLength = jvmti_agent_opts - strPtr; + jvmti_agent_opts++; + } + + char lib_name[nameLength + 3 + 1]; + strcpy (lib_name, "lib"); + strncat (lib_name, strPtr, nameLength); + jint result = load_jvmti_agent (strPtr); + + if (result < 0) + { + return -1; + } + + continue; + } else if (vm_args->ignoreUnrecognized) { if (option_string[0] == '_') @@ -1569,6 +1725,10 @@ _Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc, main_thread = new MainThread (JvNewStringUTF (name), arg_vec, is_jar); _Jv_AttachCurrentThread (main_thread); + + // Start JVMTI if an agent function has been found. + if (jvmti_agentonload) + (*jvmti_agentonload) (_Jv_GetJavaVM (), jvmti_agent_opts, NULL); // Start JDWP if (remoteDebug) @@ -1583,11 +1743,9 @@ _Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc, // Wait for JDWP to initialize and start jdwp->join (); } - - // Send VmInit - gnu::classpath::jdwp::event::VmInitEvent *event; - event = new gnu::classpath::jdwp::event::VmInitEvent (main_thread); - gnu::classpath::jdwp::Jdwp::notify (event); + // Send VMInit + if (JVMTI_REQUESTED_EVENT (VMInit)) + _Jv_JVMTI_PostEvent (JVMTI_EVENT_VM_INIT, main_thread); } catch (java::lang::Throwable *t) { @@ -1602,13 +1760,17 @@ _Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc, _Jv_ThreadRun (main_thread); - // Notify debugger of VM's death - if (gnu::classpath::jdwp::Jdwp::isDebugging) + // Send VMDeath + if (JVMTI_REQUESTED_EVENT (VMDeath)) { - using namespace gnu::classpath::jdwp; - event::VmDeathEvent *event = new event::VmDeathEvent (); - Jdwp::notify (event); + java::lang::Thread *thread = java::lang::Thread::currentThread (); + JNIEnv *jni_env = _Jv_GetCurrentJNIEnv (); + _Jv_JVMTI_PostEvent (JVMTI_EVENT_VM_DEATH, thread, jni_env); } + + // Run JVMTI AgentOnUnload if it exists and an agent is loaded. + if (jvmti_agentonunload) + (*jvmti_agentonunload) (_Jv_GetJavaVM ()); // If we got here then something went wrong, as MainThread is not // supposed to terminate. diff --git a/libjava/sources.am b/libjava/sources.am index 444060e9702..0a6adaa165f 100644 --- a/libjava/sources.am +++ b/libjava/sources.am @@ -326,15 +326,19 @@ gnu/classpath/jdwp/event/filters.list: $(gnu_classpath_jdwp_event_filters_source gnu_classpath_jdwp_exception_source_files = \ +classpath/gnu/classpath/jdwp/exception/AbsentInformationException.java \ classpath/gnu/classpath/jdwp/exception/InvalidClassException.java \ classpath/gnu/classpath/jdwp/exception/InvalidClassLoaderException.java \ classpath/gnu/classpath/jdwp/exception/InvalidCountException.java \ classpath/gnu/classpath/jdwp/exception/InvalidEventTypeException.java \ classpath/gnu/classpath/jdwp/exception/InvalidFieldException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidFrameException.java \ classpath/gnu/classpath/jdwp/exception/InvalidLocationException.java \ classpath/gnu/classpath/jdwp/exception/InvalidMethodException.java \ classpath/gnu/classpath/jdwp/exception/InvalidObjectException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidSlotException.java \ classpath/gnu/classpath/jdwp/exception/InvalidStringException.java \ +classpath/gnu/classpath/jdwp/exception/InvalidTagException.java \ classpath/gnu/classpath/jdwp/exception/InvalidThreadException.java \ classpath/gnu/classpath/jdwp/exception/InvalidThreadGroupException.java \ classpath/gnu/classpath/jdwp/exception/JdwpException.java \ @@ -342,6 +346,7 @@ classpath/gnu/classpath/jdwp/exception/JdwpIllegalArgumentException.java \ classpath/gnu/classpath/jdwp/exception/JdwpInternalErrorException.java \ classpath/gnu/classpath/jdwp/exception/NativeMethodException.java \ classpath/gnu/classpath/jdwp/exception/NotImplementedException.java \ +classpath/gnu/classpath/jdwp/exception/TypeMismatchException.java \ classpath/gnu/classpath/jdwp/exception/VmDeadException.java gnu_classpath_jdwp_exception_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_exception_source_files))) @@ -361,6 +366,7 @@ classpath/gnu/classpath/jdwp/id/ClassObjectId.java \ classpath/gnu/classpath/jdwp/id/ClassReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/InterfaceReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/JdwpId.java \ +classpath/gnu/classpath/jdwp/id/NullObjectId.java \ classpath/gnu/classpath/jdwp/id/ObjectId.java \ classpath/gnu/classpath/jdwp/id/ReferenceTypeId.java \ classpath/gnu/classpath/jdwp/id/StringId.java \ @@ -429,6 +435,8 @@ classpath/gnu/classpath/jdwp/util/JdwpString.java \ classpath/gnu/classpath/jdwp/util/LineTable.java \ classpath/gnu/classpath/jdwp/util/Location.java \ classpath/gnu/classpath/jdwp/util/MethodResult.java \ +classpath/gnu/classpath/jdwp/util/MonitorInfo.java \ +classpath/gnu/classpath/jdwp/util/NullObject.java \ classpath/gnu/classpath/jdwp/util/Signature.java \ classpath/gnu/classpath/jdwp/util/Value.java \ classpath/gnu/classpath/jdwp/util/VariableTable.java @@ -442,6 +450,31 @@ gnu/classpath/jdwp/util.list: $(gnu_classpath_jdwp_util_source_files) -include gnu/classpath/jdwp/util.deps +gnu_classpath_jdwp_value_source_files = \ +classpath/gnu/classpath/jdwp/value/ArrayValue.java \ +classpath/gnu/classpath/jdwp/value/BooleanValue.java \ +classpath/gnu/classpath/jdwp/value/ByteValue.java \ +classpath/gnu/classpath/jdwp/value/CharValue.java \ +classpath/gnu/classpath/jdwp/value/DoubleValue.java \ +classpath/gnu/classpath/jdwp/value/FloatValue.java \ +classpath/gnu/classpath/jdwp/value/IntValue.java \ +classpath/gnu/classpath/jdwp/value/LongValue.java \ +classpath/gnu/classpath/jdwp/value/ObjectValue.java \ +classpath/gnu/classpath/jdwp/value/ShortValue.java \ +classpath/gnu/classpath/jdwp/value/StringValue.java \ +classpath/gnu/classpath/jdwp/value/Value.java \ +classpath/gnu/classpath/jdwp/value/ValueFactory.java \ +classpath/gnu/classpath/jdwp/value/VoidValue.java + +gnu_classpath_jdwp_value_header_files = $(patsubst classpath/%,%,$(patsubst %.java,%.h,$(gnu_classpath_jdwp_value_source_files))) + +gnu/classpath/jdwp/value.list: $(gnu_classpath_jdwp_value_source_files) + @$(mkinstalldirs) $(dir $@) + echo $(srcdir)/classpath/lib/gnu/classpath/jdwp/value/*.class > gnu/classpath/jdwp/value.list + +-include gnu/classpath/jdwp/value.deps + + gnu_gcj_source_files = \ gnu/gcj/Core.java \ gnu/gcj/RawData.java \ @@ -8341,6 +8374,7 @@ all_packages_source_files = \ gnu/classpath/jdwp/processor.list \ gnu/classpath/jdwp/transport.list \ gnu/classpath/jdwp/util.list \ + gnu/classpath/jdwp/value.list \ gnu/gcj.list \ gnu/gcj/convert.list \ gnu/gcj/io.list \ @@ -8585,6 +8619,7 @@ ordinary_header_files = \ $(gnu_classpath_jdwp_processor_header_files) \ $(gnu_classpath_jdwp_transport_header_files) \ $(gnu_classpath_jdwp_util_header_files) \ + $(gnu_classpath_jdwp_value_header_files) \ $(gnu_gcj_header_files) \ $(gnu_gcj_convert_header_files) \ $(gnu_gcj_io_header_files) \ diff --git a/libjava/stacktrace.cc b/libjava/stacktrace.cc index 5751e29b5f1..7cf0bf1ae03 100644 --- a/libjava/stacktrace.cc +++ b/libjava/stacktrace.cc @@ -1,6 +1,6 @@ // stacktrace.cc - Functions for unwinding & inspecting the call stack. -/* Copyright (C) 2005, 2006 Free Software Foundation +/* Copyright (C) 2005, 2006, 2007 Free Software Foundation This file is part of libgcj. @@ -131,9 +131,11 @@ _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr) if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run)) { state->frames[pos].type = frame_interpreter; - state->frames[pos].interp.meth = state->interp_frame->self; + _Jv_Frame *frame = static_cast<_Jv_Frame *> (state->interp_frame); + state->frames[pos].interp.meth + = static_cast<_Jv_InterpMethod *> (frame->self); state->frames[pos].interp.pc = state->interp_frame->pc; - state->interp_frame = state->interp_frame->next; + state->interp_frame = state->interp_frame->next_interp; } else #endif @@ -143,7 +145,7 @@ _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr) state->frames[pos].type = frame_proxy; state->frames[pos].proxyClass = state->interp_frame->proxyClass; state->frames[pos].proxyMethod = state->interp_frame->proxyMethod; - state->interp_frame = state->interp_frame->next; + state->interp_frame = state->interp_frame->next_interp; } else { |