2626import java .util .Collections ;
2727import java .util .HashMap ;
2828import java .util .Iterator ;
29+ import java .util .LinkedHashSet ;
2930import java .util .List ;
3031import java .util .Map ;
32+ import java .util .Set ;
3133import java .util .concurrent .CompletableFuture ;
3234import java .util .function .Consumer ;
3335import java .util .logging .Level ;
5355import org .eclipse .jdt .core .util .IClassFileReader ;
5456import org .eclipse .jdt .core .util .ISourceAttribute ;
5557import org .eclipse .jdt .internal .core .util .Util ;
58+ import org .eclipse .jdt .ls .core .internal .JobHelpers ;
5659
5760import com .microsoft .java .debug .core .Configuration ;
5861import com .microsoft .java .debug .core .DebugException ;
5962import com .microsoft .java .debug .core .DebugSettings ;
6063import com .microsoft .java .debug .core .DebugUtility ;
6164import com .microsoft .java .debug .core .IDebugSession ;
6265import com .microsoft .java .debug .core .StackFrameUtility ;
66+ import com .microsoft .java .debug .core .adapter .AdapterUtils ;
67+ import com .microsoft .java .debug .core .adapter .ErrorCode ;
6368import com .microsoft .java .debug .core .adapter .HotCodeReplaceEvent ;
6469import com .microsoft .java .debug .core .adapter .IDebugAdapterContext ;
6570import com .microsoft .java .debug .core .adapter .IHotCodeReplaceProvider ;
@@ -96,9 +101,9 @@ public class JavaHotCodeReplaceProvider implements IHotCodeReplaceProvider, IRes
96101
97102 private PublishSubject <HotCodeReplaceEvent > eventSubject = PublishSubject .<HotCodeReplaceEvent >create ();
98103
99- private List <IResource > deltaResources = new ArrayList <>();
104+ private Set <IResource > deltaResources = new LinkedHashSet <>();
100105
101- private List <String > deltaClassNames = new ArrayList <>();
106+ private Set <String > deltaClassNames = new LinkedHashSet <>();
102107
103108 /**
104109 * Visitor for resource deltas.
@@ -300,14 +305,23 @@ public void onClassRedefined(Consumer<List<String>> consumer) {
300305
301306 @ Override
302307 public CompletableFuture <List <String >> redefineClasses () {
308+ JobHelpers .waitForBuildJobs (10 * 1000 );
303309 return CompletableFuture .supplyAsync (() -> {
304310 List <String > classNames = new ArrayList <>();
311+ List <IResource > resources = new ArrayList <>();
312+ String errorMessage = null ;
305313 synchronized (this ) {
306314 classNames .addAll (deltaClassNames );
307- doHotCodeReplace (deltaResources , deltaClassNames );
315+ resources . addAll (deltaResources );
308316 deltaResources .clear ();
309317 deltaClassNames .clear ();
318+ errorMessage = doHotCodeReplace (resources , classNames );
310319 }
320+
321+ if (!classNames .isEmpty () && errorMessage != null ) {
322+ throw AdapterUtils .createCompletionException (errorMessage , ErrorCode .HCR_FAILURE );
323+ }
324+
311325 return classNames ;
312326 });
313327 }
@@ -325,27 +339,29 @@ private void publishEvent(HotCodeReplaceEvent.EventType type, String message, Ob
325339 eventSubject .onNext (new HotCodeReplaceEvent (type , message , data ));
326340 }
327341
328- private void doHotCodeReplace (List <IResource > resourcesToReplace , List <String > qualifiedNamesToReplace ) {
342+ private String doHotCodeReplace (List <IResource > resourcesToReplace , List <String > qualifiedNamesToReplace ) {
329343 if (context == null || currentDebugSession == null ) {
330- return ;
344+ return null ;
331345 }
332346
333347 if (resourcesToReplace == null || qualifiedNamesToReplace == null || qualifiedNamesToReplace .isEmpty ()
334348 || resourcesToReplace .isEmpty ()) {
335- return ;
349+ return null ;
336350 }
337351
338352 filterNotLoadedTypes (resourcesToReplace , qualifiedNamesToReplace );
339353 if (qualifiedNamesToReplace .isEmpty ()) {
340- return ;
354+ return null ;
341355 // If none of the changed types are loaded, do nothing.
342356 }
343357
344358 // Not supported scenario:
345359 if (!currentDebugSession .getVM ().canRedefineClasses ()) {
346- return ;
360+ publishEvent (HotCodeReplaceEvent .EventType .ERROR , "JVM doesn't support hot reload classes" );
361+ return "JVM doesn't support hot reload classes" ;
347362 }
348363
364+ String errorMessage = null ;
349365 publishEvent (HotCodeReplaceEvent .EventType .STARTING , "Start hot code replacement procedure..." );
350366
351367 try {
@@ -367,6 +383,7 @@ private void doHotCodeReplace(List<IResource> resourcesToReplace, List<String> q
367383
368384 if (containsObsoleteMethods ()) {
369385 publishEvent (HotCodeReplaceEvent .EventType .ERROR , "JVM contains obsolete methods" );
386+ errorMessage = "JVM contains obsolete methods" ;
370387 }
371388
372389 if (currentDebugSession .getVM ().canPopFrames () && framesPopped ) {
@@ -376,11 +393,13 @@ private void doHotCodeReplace(List<IResource> resourcesToReplace, List<String> q
376393 }
377394 } catch (DebugException e ) {
378395 logger .log (Level .SEVERE , "Failed to complete hot code replace: " + e .getMessage (), e );
396+ errorMessage = e .getMessage ();
379397 } finally {
380398 publishEvent (HotCodeReplaceEvent .EventType .END , "Completed hot code replace" , qualifiedNamesToReplace );
399+ threadFrameMap .clear ();
381400 }
382401
383- threadFrameMap . clear () ;
402+ return errorMessage ;
384403 }
385404
386405 private void filterNotLoadedTypes (List <IResource > resources , List <String > qualifiedNames ) {
0 commit comments