3737public class Breakpoint implements IBreakpoint {
3838 private VirtualMachine vm = null ;
3939 private IEventHub eventHub = null ;
40- private String className = null ;
41- private int lineNumber = 0 ;
40+ private JavaBreakpointLocation sourceLocation = null ;
4241 private int hitCount = 0 ;
4342 private String condition = null ;
4443 private String logMessage = null ;
4544 private HashMap <Object , Object > propertyMap = new HashMap <>();
46- private String methodSignature = null ;
4745
4846 private boolean async = false ;
4947
@@ -56,21 +54,37 @@ public class Breakpoint implements IBreakpoint {
5654 }
5755
5856 Breakpoint (VirtualMachine vm , IEventHub eventHub , String className , int lineNumber , int hitCount , String condition ) {
57+ this (vm , eventHub , className , lineNumber , hitCount , condition , null );
58+ }
59+
60+ Breakpoint (VirtualMachine vm , IEventHub eventHub , String className , int lineNumber , int hitCount , String condition , String logMessage ) {
5961 this .vm = vm ;
6062 this .eventHub = eventHub ;
63+ String contextClass = className ;
64+ String methodName = null ;
65+ String methodSignature = null ;
6166 if (className != null && className .contains ("#" )) {
62- this . className = className .substring (0 , className .indexOf ("#" ));
63- this . methodSignature = className .substring (className .indexOf ("#" ) + 1 );
64- } else {
65- this . className = className ;
67+ contextClass = className .substring (0 , className .indexOf ("#" ));
68+ String [] methodInfo = className .substring (className .indexOf ("#" ) + 1 ). split ( "#" );
69+ methodName = methodInfo [ 0 ];
70+ methodSignature = methodInfo [ 1 ] ;
6671 }
67- this .lineNumber = lineNumber ;
72+
73+ this .sourceLocation = new JavaBreakpointLocation (lineNumber , -1 );
74+ this .sourceLocation .setClassName (contextClass );
75+ this .sourceLocation .setMethodName (methodName );
76+ this .sourceLocation .setMethodSignature (methodSignature );
6877 this .hitCount = hitCount ;
6978 this .condition = condition ;
79+ this .logMessage = logMessage ;
7080 }
7181
72- Breakpoint (VirtualMachine vm , IEventHub eventHub , String className , int lineNumber , int hitCount , String condition , String logMessage ) {
73- this (vm , eventHub , className , lineNumber , hitCount , condition );
82+ Breakpoint (VirtualMachine vm , IEventHub eventHub , JavaBreakpointLocation sourceLocation , int hitCount , String condition , String logMessage ) {
83+ this .vm = vm ;
84+ this .eventHub = eventHub ;
85+ this .sourceLocation = sourceLocation ;
86+ this .hitCount = hitCount ;
87+ this .condition = condition ;
7488 this .logMessage = logMessage ;
7589 }
7690
@@ -104,14 +118,24 @@ public void close() throws Exception {
104118 }
105119
106120 // IBreakpoint
121+ @ Override
122+ public JavaBreakpointLocation sourceLocation () {
123+ return this .sourceLocation ;
124+ }
125+
107126 @ Override
108127 public String className () {
109- return className ;
128+ return this . sourceLocation . className () ;
110129 }
111130
112131 @ Override
113132 public int getLineNumber () {
114- return lineNumber ;
133+ return this .sourceLocation .lineNumber ();
134+ }
135+
136+ @ Override
137+ public int getColumnNumber () {
138+ return this .sourceLocation .columnNumber ();
115139 }
116140
117141 @ Override
@@ -120,20 +144,20 @@ public String getCondition() {
120144 }
121145
122146 @ Override
123- public boolean equals (Object obj ) {
124- if (!(obj instanceof Breakpoint )) {
125- return super .equals (obj );
126- }
127-
128- Breakpoint breakpoint = (Breakpoint ) obj ;
129- return Objects .equals (this .className (), breakpoint .className ())
130- && this .getLineNumber () == breakpoint .getLineNumber ()
131- && Objects .equals (this .methodSignature , breakpoint .methodSignature );
147+ public int hashCode () {
148+ return Objects .hash (sourceLocation );
132149 }
133150
134151 @ Override
135- public int hashCode () {
136- return Objects .hash (this .className , this .lineNumber , this .methodSignature );
152+ public boolean equals (Object obj ) {
153+ if (this == obj ) {
154+ return true ;
155+ }
156+ if (!(obj instanceof Breakpoint )) {
157+ return false ;
158+ }
159+ Breakpoint other = (Breakpoint ) obj ;
160+ return Objects .equals (sourceLocation , other .sourceLocation );
137161 }
138162
139163 @ Override
@@ -149,6 +173,7 @@ public void setHitCount(int hitCount) {
149173 .filter (request -> request instanceof BreakpointRequest )
150174 .subscribe (request -> {
151175 request .addCountFilter (hitCount );
176+ request .disable ();
152177 request .enable ();
153178 });
154179 }
@@ -183,13 +208,13 @@ public CompletableFuture<IBreakpoint> install() {
183208 // It's possible that different class loaders create new class with the same name.
184209 // Here to listen to future class prepare events to handle such case.
185210 ClassPrepareRequest classPrepareRequest = vm .eventRequestManager ().createClassPrepareRequest ();
186- classPrepareRequest .addClassFilter (className );
211+ classPrepareRequest .addClassFilter (className () );
187212 classPrepareRequest .enable ();
188213 requests .add (classPrepareRequest );
189214
190215 // Local types also needs to be handled
191216 ClassPrepareRequest localClassPrepareRequest = vm .eventRequestManager ().createClassPrepareRequest ();
192- localClassPrepareRequest .addClassFilter (className + "$*" );
217+ localClassPrepareRequest .addClassFilter (className () + "$*" );
193218 localClassPrepareRequest .enable ();
194219 requests .add (localClassPrepareRequest );
195220
@@ -202,7 +227,7 @@ public CompletableFuture<IBreakpoint> install() {
202227 .subscribe (debugEvent -> {
203228 ClassPrepareEvent event = (ClassPrepareEvent ) debugEvent .event ;
204229 List <BreakpointRequest > newRequests = AsyncJdwpUtils .await (
205- createBreakpointRequests (event .referenceType (), lineNumber , hitCount , false )
230+ createBreakpointRequests (event .referenceType (), getLineNumber () , hitCount , false )
206231 );
207232 requests .addAll (newRequests );
208233 if (!newRequests .isEmpty () && !future .isDone ()) {
@@ -213,8 +238,8 @@ public CompletableFuture<IBreakpoint> install() {
213238 subscriptions .add (subscription );
214239
215240 Runnable resolveRequestsFromExistingClasses = () -> {
216- List <ReferenceType > refTypes = vm .classesByName (className );
217- createBreakpointRequests (refTypes , lineNumber , hitCount , true )
241+ List <ReferenceType > refTypes = vm .classesByName (className () );
242+ createBreakpointRequests (refTypes , getLineNumber () , hitCount , true )
218243 .whenComplete ((newRequests , ex ) -> {
219244 if (ex != null ) {
220245 return ;
@@ -281,14 +306,13 @@ private CompletableFuture<List<Location>> collectLocations(ReferenceType refType
281306 });
282307 }
283308
284- private CompletableFuture <List <Location >> collectLocations (List <ReferenceType > refTypes , String nameAndSignature ) {
285- String [] segments = nameAndSignature .split ("#" );
309+ private CompletableFuture <List <Location >> collectLocations (List <ReferenceType > refTypes , String methodName , String methodSiguature ) {
286310 List <CompletableFuture <Location >> futures = new ArrayList <>();
287311 for (ReferenceType refType : refTypes ) {
288312 if (async ()) {
289- futures .add (AsyncJdwpUtils .supplyAsync (() -> findMethodLocaiton (refType , segments [ 0 ], segments [ 1 ] )));
313+ futures .add (AsyncJdwpUtils .supplyAsync (() -> findMethodLocaiton (refType , methodName , methodSiguature )));
290314 } else {
291- futures .add (CompletableFuture .completedFuture (findMethodLocaiton (refType , segments [ 0 ], segments [ 1 ] )));
315+ futures .add (CompletableFuture .completedFuture (findMethodLocaiton (refType , methodName , methodSiguature )));
292316 }
293317 }
294318
@@ -329,10 +353,22 @@ private CompletableFuture<List<BreakpointRequest>> createBreakpointRequests(Refe
329353 private CompletableFuture <List <BreakpointRequest >> createBreakpointRequests (List <ReferenceType > refTypes , int lineNumber ,
330354 int hitCount , boolean includeNestedTypes ) {
331355 CompletableFuture <List <Location >> locationsFuture ;
332- if (this .methodSignature != null ) {
333- locationsFuture = collectLocations (refTypes , this .methodSignature );
356+ if (this .sourceLocation . methodName () != null ) {
357+ locationsFuture = collectLocations (refTypes , this .sourceLocation . methodName (), this . sourceLocation . methodSignature () );
334358 } else {
335- locationsFuture = collectLocations (refTypes , lineNumber , includeNestedTypes );
359+ locationsFuture = collectLocations (refTypes , lineNumber , includeNestedTypes ).thenApply ((locations ) -> {
360+ if (locations .isEmpty ()) {
361+ return locations ;
362+ }
363+
364+ /**
365+ * For a line breakpoint, we default to breaking at the first location
366+ * of the line. If you want to break at other locations on the same line,
367+ * you can add an inline breakpoint based on the locations returned by
368+ * the BreakpointLocation request.
369+ */
370+ return Arrays .asList (locations .get (0 ));
371+ });
336372 }
337373
338374 return locationsFuture .thenCompose ((locations ) -> {
@@ -389,11 +425,11 @@ private CompletableFuture<List<BreakpointRequest>> createBreakpointRequests(List
389425 }
390426
391427 private Object computeRequestType () {
392- if (this .methodSignature == null ) {
428+ if (this .sourceLocation . methodName () == null ) {
393429 return IBreakpoint .REQUEST_TYPE_LINE ;
394430 }
395431
396- if (this .methodSignature .startsWith ("lambda$" )) {
432+ if (this .sourceLocation . methodName () .startsWith ("lambda$" )) {
397433 return IBreakpoint .REQUEST_TYPE_LAMBDA ;
398434 } else {
399435 return IBreakpoint .REQUEST_TYPE_METHOD ;
0 commit comments