src/jdk.jshell/share/classes/jdk/jshell/Unit.java

Print this page




  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jshell;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.Collections;
  31 import java.util.LinkedHashSet;
  32 import java.util.List;
  33 import java.util.Set;
  34 import java.util.stream.Stream;

  35 import jdk.jshell.Snippet.Kind;
  36 import jdk.jshell.Snippet.Status;
  37 import jdk.jshell.Snippet.SubKind;
  38 import jdk.jshell.TaskFactory.AnalyzeTask;
  39 import jdk.jshell.TaskFactory.CompileTask;



  40 import static java.util.stream.Collectors.toList;
  41 import static java.util.stream.Collectors.toSet;
  42 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
  43 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  44 import static jdk.jshell.Snippet.Status.OVERWRITTEN;
  45 import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED;
  46 import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
  47 import static jdk.jshell.Snippet.Status.REJECTED;
  48 import static jdk.jshell.Snippet.Status.VALID;
  49 import static jdk.jshell.Util.PARSED_LOCALE;
  50 import static jdk.jshell.Util.expunge;
  51 
  52 /**
  53  * Tracks the compilation and load of a new or updated snippet.
  54  * @author Robert Field
  55  */
  56 final class Unit {
  57 
  58     private final JShell state;
  59     private final Snippet si;
  60     private final Snippet siOld;
  61     private final boolean isDependency;
  62     private final boolean isNew;
  63     private final Snippet causalSnippet;
  64     private final DiagList generatedDiagnostics;
  65 
  66     private int seq;
  67     private String classNameInitial;
  68     private Wrap activeGuts;
  69     private Status status;
  70     private Status prevStatus;
  71     private boolean signatureChanged;
  72     private DiagList compilationDiagnostics;
  73     private DiagList recompilationDiagnostics = null;
  74     private List<String> unresolved;
  75     private SnippetEvent replaceOldEvent;
  76     private List<SnippetEvent> secondaryEvents;
  77     private boolean isAttemptingCorral;
  78     private List<String> toRedefine;
  79     private boolean dependenciesNeeded;
  80 
  81     Unit(JShell state, Snippet si, Snippet causalSnippet,
  82             DiagList generatedDiagnostics) {
  83         this.state = state;
  84         this.si = si;
  85         this.isDependency = causalSnippet != null;
  86         this.siOld = isDependency
  87                 ? si
  88                 : state.maps.getSnippet(si.key());
  89         this.isNew = siOld == null;
  90         this.causalSnippet = causalSnippet;
  91         this.generatedDiagnostics = generatedDiagnostics;
  92 
  93         this.seq = isNew? 0 : siOld.sequenceNumber();
  94         this.classNameInitial = isNew? "<none>" : siOld.className();
  95         this.prevStatus = (isNew || isDependency)
  96                 ? si.status()
  97                 : siOld.status();
  98         si.setSequenceNumber(seq);


 244         } else if (isRecoverable()) {
 245             if (isAttemptingCorral && !recompilationDiagnostics.hasErrors()) {
 246                 status = RECOVERABLE_DEFINED;
 247             } else {
 248                 status = RECOVERABLE_NOT_DEFINED;
 249             }
 250         } else {
 251             status = REJECTED;
 252         }
 253         checkForOverwrite(at);
 254 
 255         state.debug(DBG_GEN, "setStatus() %s - status: %s\n",
 256                 si, status);
 257     }
 258 
 259     boolean isDefined() {
 260         return status.isDefined();
 261     }
 262 
 263     /**
 264      * Process the class information from the last compile.
 265      * Requires loading of returned list.

 266      * @return the list of classes to load
 267      */
 268     Stream<String> classesToLoad(List<String> classnames) {
 269         toRedefine = new ArrayList<>();
 270         List<String> toLoad = new ArrayList<>();
 271         if (status.isDefined() && !isImport()) {
 272             // Classes should only be loaded/redefined if the compile left them
 273             // in a defined state.  Imports do not have code and are not loaded.
 274             for (String cn : classnames) {
 275                 switch (state.executionControl().getClassStatus(cn)) {
 276                     case UNKNOWN:






 277                         // If not loaded, add to the list of classes to load.
 278                         toLoad.add(cn);
 279                         dependenciesNeeded = true;
 280                         break;
 281                     case NOT_CURRENT:
 282                         // If loaded but out of date, add to the list of classes to attempt redefine.
 283                         toRedefine.add(cn);
 284                         break;
 285                     case CURRENT:
 286                         // Loaded and current, so nothing to do
 287                         break;
 288                 }
 289             }
 290         }
 291         return toLoad.stream();
 292     }
 293 
 294     /**
 295      * Redefine classes needing redefine.
 296      * classesToLoad() must be called first.
 297      * @return true if all redefines succeeded (can be vacuously true)
 298      */
 299     boolean doRedefines() {
 300         return toRedefine.isEmpty()
 301                 ? true
 302                 : state.executionControl().redefine(toRedefine);














 303     }
 304 
 305     void markForReplacement() {
 306         // increment for replace class wrapper
 307         si.setSequenceNumber(++seq);
 308     }
 309 
 310     private boolean isImport() {
 311         return si.kind() == Kind.IMPORT;
 312     }
 313 
 314     private boolean sigChanged() {
 315         return (status.isDefined() != prevStatus.isDefined())
 316                 || (status.isDefined() && !si.className().equals(classNameInitial))
 317                 || signatureChanged;
 318     }
 319 
 320     Stream<Unit> effectedDependents() {
 321         //System.err.printf("effectedDependents sigChanged=%b  dependenciesNeeded=%b   status=%s\n",
 322         //       sigChanged(), dependenciesNeeded, status);




  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jshell;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collection;
  30 import java.util.Collections;
  31 import java.util.LinkedHashSet;
  32 import java.util.List;
  33 import java.util.Set;
  34 import java.util.stream.Stream;
  35 import jdk.jshell.ClassTracker.ClassInfo;
  36 import jdk.jshell.Snippet.Kind;
  37 import jdk.jshell.Snippet.Status;
  38 import jdk.jshell.Snippet.SubKind;
  39 import jdk.jshell.TaskFactory.AnalyzeTask;
  40 import jdk.jshell.TaskFactory.CompileTask;
  41 import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
  42 import jdk.jshell.spi.ExecutionControl.ClassInstallException;
  43 import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
  44 import static java.util.stream.Collectors.toList;
  45 import static java.util.stream.Collectors.toSet;
  46 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
  47 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  48 import static jdk.jshell.Snippet.Status.OVERWRITTEN;
  49 import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED;
  50 import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
  51 import static jdk.jshell.Snippet.Status.REJECTED;
  52 import static jdk.jshell.Snippet.Status.VALID;
  53 import static jdk.jshell.Util.PARSED_LOCALE;
  54 import static jdk.jshell.Util.expunge;
  55 
  56 /**
  57  * Tracks the compilation and load of a new or updated snippet.
  58  * @author Robert Field
  59  */
  60 final class Unit {
  61 
  62     private final JShell state;
  63     private final Snippet si;
  64     private final Snippet siOld;
  65     private final boolean isDependency;
  66     private final boolean isNew;
  67     private final Snippet causalSnippet;
  68     private final DiagList generatedDiagnostics;
  69 
  70     private int seq;
  71     private String classNameInitial;
  72     private Wrap activeGuts;
  73     private Status status;
  74     private Status prevStatus;
  75     private boolean signatureChanged;
  76     private DiagList compilationDiagnostics;
  77     private DiagList recompilationDiagnostics = null;
  78     private List<String> unresolved;
  79     private SnippetEvent replaceOldEvent;
  80     private List<SnippetEvent> secondaryEvents;
  81     private boolean isAttemptingCorral;
  82     private List<ClassInfo> toRedefine;
  83     private boolean dependenciesNeeded;
  84 
  85     Unit(JShell state, Snippet si, Snippet causalSnippet,
  86             DiagList generatedDiagnostics) {
  87         this.state = state;
  88         this.si = si;
  89         this.isDependency = causalSnippet != null;
  90         this.siOld = isDependency
  91                 ? si
  92                 : state.maps.getSnippet(si.key());
  93         this.isNew = siOld == null;
  94         this.causalSnippet = causalSnippet;
  95         this.generatedDiagnostics = generatedDiagnostics;
  96 
  97         this.seq = isNew? 0 : siOld.sequenceNumber();
  98         this.classNameInitial = isNew? "<none>" : siOld.className();
  99         this.prevStatus = (isNew || isDependency)
 100                 ? si.status()
 101                 : siOld.status();
 102         si.setSequenceNumber(seq);


 248         } else if (isRecoverable()) {
 249             if (isAttemptingCorral && !recompilationDiagnostics.hasErrors()) {
 250                 status = RECOVERABLE_DEFINED;
 251             } else {
 252                 status = RECOVERABLE_NOT_DEFINED;
 253             }
 254         } else {
 255             status = REJECTED;
 256         }
 257         checkForOverwrite(at);
 258 
 259         state.debug(DBG_GEN, "setStatus() %s - status: %s\n",
 260                 si, status);
 261     }
 262 
 263     boolean isDefined() {
 264         return status.isDefined();
 265     }
 266 
 267     /**
 268      * Process the class information from the last compile. Requires loading of
 269      * returned list.
 270      *
 271      * @return the list of classes to load
 272      */
 273     Stream<ClassBytecodes> classesToLoad(List<String> classnames) {
 274         toRedefine = new ArrayList<>();
 275         List<ClassBytecodes> toLoad = new ArrayList<>();
 276         if (status.isDefined() && !isImport()) {
 277             // Classes should only be loaded/redefined if the compile left them
 278             // in a defined state.  Imports do not have code and are not loaded.
 279             for (String cn : classnames) {
 280                 ClassInfo ci = state.classTracker.get(cn);
 281                 if (ci.isLoaded()) {
 282                     if (ci.isCurrent()) {
 283                         // nothing to do
 284                     } else {
 285                         toRedefine.add(ci);
 286                     }
 287                 } else {
 288                     // If not loaded, add to the list of classes to load.
 289                     toLoad.add(ci.toClassBytecodes());
 290                     dependenciesNeeded = true;








 291                 }
 292             }
 293         }
 294         return toLoad.stream();
 295     }
 296 
 297     /**
 298      * Redefine classes needing redefine. classesToLoad() must be called first.
 299      *
 300      * @return true if all redefines succeeded (can be vacuously true)
 301      */
 302     boolean doRedefines() {
 303         if (toRedefine.isEmpty()) {
 304             return true;
 305         }
 306         ClassBytecodes[] cbcs = toRedefine.stream()
 307                 .map(ci -> ci.toClassBytecodes())
 308                 .toArray(size -> new ClassBytecodes[size]);
 309         try {
 310             state.executionControl().redefine(cbcs);
 311             state.classTracker.markLoaded(cbcs);
 312             return true;
 313         } catch (ClassInstallException ex) {
 314             state.classTracker.markLoaded(cbcs, ex.installed());
 315             return false;
 316         } catch (EngineTerminationException ex) {
 317             state.closeDown();
 318             return false;
 319         }
 320     }
 321 
 322     void markForReplacement() {
 323         // increment for replace class wrapper
 324         si.setSequenceNumber(++seq);
 325     }
 326 
 327     private boolean isImport() {
 328         return si.kind() == Kind.IMPORT;
 329     }
 330 
 331     private boolean sigChanged() {
 332         return (status.isDefined() != prevStatus.isDefined())
 333                 || (status.isDefined() && !si.className().equals(classNameInitial))
 334                 || signatureChanged;
 335     }
 336 
 337     Stream<Unit> effectedDependents() {
 338         //System.err.printf("effectedDependents sigChanged=%b  dependenciesNeeded=%b   status=%s\n",
 339         //       sigChanged(), dependenciesNeeded, status);