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


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








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