< prev index next >

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

Print this page




  81 import static java.util.Collections.singletonList;
  82 import com.sun.tools.javac.code.Symbol.TypeSymbol;
  83 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  84 import static jdk.jshell.Util.DOIT_METHOD_NAME;
  85 import static jdk.jshell.Util.PREFIX_PATTERN;
  86 import static jdk.jshell.Util.expunge;
  87 import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
  88 import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
  89 import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
  90 import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
  91 
  92 /**
  93  * The Evaluation Engine. Source internal analysis, wrapping control,
  94  * compilation, declaration. redefinition, replacement, and execution.
  95  *
  96  * @author Robert Field
  97  */
  98 class Eval {
  99 
 100     private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\p{javaWhitespace}+(?<static>static\\p{javaWhitespace}+)?(?<fullname>[\\p{L}\\p{N}_\\$\\.]+\\.(?<name>[\\p{L}\\p{N}_\\$]+|\\*))");
 101     private static final Pattern DEFAULT_PREFIX = Pattern.compile("\\p{javaWhitespace}*(default)\\p{javaWhitespace}+");
 102 
 103     // for uses that should not change state -- non-evaluations
 104     private boolean preserveState = false;
 105 
 106     private int varNumber = 0;
 107 
 108     /* The number of anonymous innerclasses seen so far. Used to generate unique
 109      * names of these classes.
 110      */
 111     private int anonCount = 0;
 112 
 113     private final JShell state;
 114 
 115     // The set of names of methods on Object
 116     private final Set<String> objectMethods = Arrays
 117             .stream(Object.class.getMethods())
 118             .map(m -> m.getName())
 119             .collect(toSet());
 120 
 121     Eval(JShell state) {


 185     /**
 186      * Converts the user source of a snippet into a Snippet object (or list of
 187      * objects in the case of: int x, y, z;).  Does not install the Snippets
 188      * or execute them.
 189      *
 190      * @param userSource the source of the snippet
 191      * @return usually a singleton list of Snippet, but may be empty or multiple
 192      */
 193     private List<Snippet> sourceToSnippets(String userSource) {
 194         String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
 195         if (compileSource.length() == 0) {
 196             return Collections.emptyList();
 197         }
 198         return state.taskFactory.parse(compileSource, pt -> {
 199             List<? extends Tree> units = pt.units();
 200             if (units.isEmpty()) {
 201                 return compileFailResult(pt, userSource, Kind.ERRONEOUS);
 202             }
 203             Tree unitTree = units.get(0);
 204             if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
 205                 Matcher matcher = DEFAULT_PREFIX.matcher(compileSource);
 206                 DiagList dlist = matcher.lookingAt()
 207                         ? new DiagList(new ModifierDiagnostic(true,
 208                             state.messageFormat("jshell.diag.modifier.single.fatal", "'default'"),
 209                             matcher.start(1), matcher.end(1)))
 210                         : pt.getDiagnostics();
 211                 return compileFailResult(dlist, userSource, kindOfTree(unitTree));
 212             }
 213 
 214             // Erase illegal/ignored modifiers
 215             String compileSourceInt = new MaskCommentsAndModifiers(compileSource, true).cleared();
 216 
 217             state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
 218             switch (unitTree.getKind()) {
 219                 case IMPORT:
 220                     return processImport(userSource, compileSourceInt);
 221                 case VARIABLE:
 222                     return processVariables(userSource, units, compileSourceInt, pt);
 223                 case EXPRESSION_STATEMENT:
 224                     return processExpression(userSource, unitTree, compileSourceInt, pt);
 225                 case CLASS:
 226                     return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
 227                 case ENUM:
 228                     return processClass(userSource, unitTree, compileSourceInt, SubKind.ENUM_SUBKIND, pt);
 229                 case ANNOTATION_TYPE:
 230                     return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
 231                 case INTERFACE:


 957         return units.stream()
 958                 .map(u -> u.snippet().outerWrap())
 959                 .collect(toSet());
 960     }
 961 
 962     private Set<Unit> compileAndLoad(Set<Unit> ins) {
 963         if (ins.isEmpty()) {
 964             return ins;
 965         }
 966         Set<Unit> replaced = new LinkedHashSet<>();
 967         // Loop until dependencies and errors are stable
 968         while (true) {
 969             state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
 970 
 971             ins.stream().forEach(Unit::initialize);
 972             ins.stream().forEach(u -> u.setWrap(ins, ins));
 973             state.taskFactory.analyze(outerWrapSet(ins), at -> {
 974                 ins.stream().forEach(u -> u.setDiagnostics(at));
 975 
 976                 // corral any Snippets that need it
 977                 if (ins.stream().filter(u -> u.corralIfNeeded(ins)).count() > 0) {
 978                     // if any were corralled, re-analyze everything
 979                     state.taskFactory.analyze(outerWrapSet(ins), cat -> {
 980                         ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
 981                         ins.stream().forEach(u -> u.setStatus(cat));
 982                         return null;
 983                     });
 984                 } else {
 985                     ins.stream().forEach(u -> u.setStatus(at));
 986                 }
 987                 return null;
 988             });
 989             // compile and load the legit snippets
 990             boolean success;
 991             while (true) {
 992                 List<Unit> legit = ins.stream()
 993                         .filter(Unit::isDefined)
 994                         .collect(toList());
 995                 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
 996                         ins, legit);
 997                 if (legit.isEmpty()) {


1144             }
1145 
1146             @Override
1147             public long getEndPosition() {
1148                 return nameStart + name.length();
1149             }
1150 
1151             @Override
1152             public String getCode() {
1153                 return "jdk.eval.error.object.method";
1154             }
1155 
1156             @Override
1157             public String getMessage(Locale locale) {
1158                 return state.messageFormat("jshell.diag.object.method.fatal",
1159                         String.join(" ", objectMethods));
1160             }
1161         };
1162     }
1163 
1164     private class ModifierDiagnostic extends Diag {



1165 
1166             final boolean fatal;
1167             final String message;
1168             final long start;
1169             final long end;
1170 
1171             ModifierDiagnostic(boolean fatal,
1172                     final String message,
1173                     long start,
1174                     long end) {
1175                 this.fatal = fatal;
1176                 this.message = message;
1177                 this.start = start;
1178                 this.end = end;













1179             }
1180 
1181             @Override
1182             public boolean isError() {
1183                 return fatal;
1184             }
1185 
1186             @Override
1187             public long getPosition() {
1188                 return start;
1189             }
1190 
1191             @Override
1192             public long getStartPosition() {
1193                 return start;
1194             }
1195 
1196             @Override
1197             public long getEndPosition() {
1198                 return end;
1199             }
1200 
1201             @Override
1202             public String getCode() {
1203                 return fatal
1204                         ? "jdk.eval.error.illegal.modifiers"
1205                         : "jdk.eval.warn.illegal.modifiers";
1206             }
1207 
1208             @Override
1209             public String getMessage(Locale locale) {
1210                 return message;
1211             }
1212     }
1213 
1214     private DiagList modifierDiagnostics(ModifiersTree modtree,
1215                                          final TreeDissector dis, boolean isAbstractProhibited) {
1216 
1217         List<Modifier> list = new ArrayList<>();
1218         boolean fatal = false;
1219         for (Modifier mod : modtree.getFlags()) {
1220             switch (mod) {
1221                 case SYNCHRONIZED:
1222                 case NATIVE:
1223                     list.add(mod);
1224                     fatal = true;
1225                     break;
1226                 case ABSTRACT:
1227                     if (isAbstractProhibited) {
1228                         list.add(mod);
1229                         fatal = true;
1230                     }
1231                     break;
1232                 case PUBLIC:
1233                 case PROTECTED:
1234                 case PRIVATE:
1235                     // quietly ignore, user cannot see effects one way or the other
1236                     break;
1237                 case STATIC:
1238                 case FINAL:
1239                     list.add(mod);
1240                     break;
1241             }
1242         }
1243         if (list.isEmpty()) {
1244             return new DiagList();
1245         } else {
1246             StringBuilder sb = new StringBuilder();
1247             for (Modifier mod : list) {
1248                 sb.append("'");
1249                 sb.append(mod.toString());
1250                 sb.append("' ");
1251             }
1252             String key = (list.size() > 1)
1253                     ? fatal
1254                     ? "jshell.diag.modifier.plural.fatal"
1255                     : "jshell.diag.modifier.plural.ignore"
1256                     : fatal
1257                     ? "jshell.diag.modifier.single.fatal"
1258                     : "jshell.diag.modifier.single.ignore";
1259             String message = state.messageFormat(key, sb.toString().trim());
1260             return new DiagList(new ModifierDiagnostic(fatal, message,
1261                     dis.getStartPosition(modtree), dis.getEndPosition(modtree)));
1262         }
1263     }
1264 
1265     String computeDeclareName(TypeSymbol ts) {
1266         return Util.JSHELL_ANONYMOUS + "$" + Long.toUnsignedString(anonCount++);
1267     }
1268 }


  81 import static java.util.Collections.singletonList;
  82 import com.sun.tools.javac.code.Symbol.TypeSymbol;
  83 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
  84 import static jdk.jshell.Util.DOIT_METHOD_NAME;
  85 import static jdk.jshell.Util.PREFIX_PATTERN;
  86 import static jdk.jshell.Util.expunge;
  87 import static jdk.jshell.Snippet.SubKind.SINGLE_TYPE_IMPORT_SUBKIND;
  88 import static jdk.jshell.Snippet.SubKind.SINGLE_STATIC_IMPORT_SUBKIND;
  89 import static jdk.jshell.Snippet.SubKind.TYPE_IMPORT_ON_DEMAND_SUBKIND;
  90 import static jdk.jshell.Snippet.SubKind.STATIC_IMPORT_ON_DEMAND_SUBKIND;
  91 
  92 /**
  93  * The Evaluation Engine. Source internal analysis, wrapping control,
  94  * compilation, declaration. redefinition, replacement, and execution.
  95  *
  96  * @author Robert Field
  97  */
  98 class Eval {
  99 
 100     private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\p{javaWhitespace}+(?<static>static\\p{javaWhitespace}+)?(?<fullname>[\\p{L}\\p{N}_\\$\\.]+\\.(?<name>[\\p{L}\\p{N}_\\$]+|\\*))");

 101 
 102     // for uses that should not change state -- non-evaluations
 103     private boolean preserveState = false;
 104 
 105     private int varNumber = 0;
 106 
 107     /* The number of anonymous innerclasses seen so far. Used to generate unique
 108      * names of these classes.
 109      */
 110     private int anonCount = 0;
 111 
 112     private final JShell state;
 113 
 114     // The set of names of methods on Object
 115     private final Set<String> objectMethods = Arrays
 116             .stream(Object.class.getMethods())
 117             .map(m -> m.getName())
 118             .collect(toSet());
 119 
 120     Eval(JShell state) {


 184     /**
 185      * Converts the user source of a snippet into a Snippet object (or list of
 186      * objects in the case of: int x, y, z;).  Does not install the Snippets
 187      * or execute them.
 188      *
 189      * @param userSource the source of the snippet
 190      * @return usually a singleton list of Snippet, but may be empty or multiple
 191      */
 192     private List<Snippet> sourceToSnippets(String userSource) {
 193         String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared());
 194         if (compileSource.length() == 0) {
 195             return Collections.emptyList();
 196         }
 197         return state.taskFactory.parse(compileSource, pt -> {
 198             List<? extends Tree> units = pt.units();
 199             if (units.isEmpty()) {
 200                 return compileFailResult(pt, userSource, Kind.ERRONEOUS);
 201             }
 202             Tree unitTree = units.get(0);
 203             if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) {
 204                 return compileFailResult(pt, userSource, kindOfTree(unitTree));






 205             }
 206 
 207             // Erase illegal/ignored modifiers
 208             String compileSourceInt = new MaskCommentsAndModifiers(compileSource, true).cleared();
 209 
 210             state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree);
 211             switch (unitTree.getKind()) {
 212                 case IMPORT:
 213                     return processImport(userSource, compileSourceInt);
 214                 case VARIABLE:
 215                     return processVariables(userSource, units, compileSourceInt, pt);
 216                 case EXPRESSION_STATEMENT:
 217                     return processExpression(userSource, unitTree, compileSourceInt, pt);
 218                 case CLASS:
 219                     return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
 220                 case ENUM:
 221                     return processClass(userSource, unitTree, compileSourceInt, SubKind.ENUM_SUBKIND, pt);
 222                 case ANNOTATION_TYPE:
 223                     return processClass(userSource, unitTree, compileSourceInt, SubKind.ANNOTATION_TYPE_SUBKIND, pt);
 224                 case INTERFACE:


 950         return units.stream()
 951                 .map(u -> u.snippet().outerWrap())
 952                 .collect(toSet());
 953     }
 954 
 955     private Set<Unit> compileAndLoad(Set<Unit> ins) {
 956         if (ins.isEmpty()) {
 957             return ins;
 958         }
 959         Set<Unit> replaced = new LinkedHashSet<>();
 960         // Loop until dependencies and errors are stable
 961         while (true) {
 962             state.debug(DBG_GEN, "compileAndLoad  %s\n", ins);
 963 
 964             ins.stream().forEach(Unit::initialize);
 965             ins.stream().forEach(u -> u.setWrap(ins, ins));
 966             state.taskFactory.analyze(outerWrapSet(ins), at -> {
 967                 ins.stream().forEach(u -> u.setDiagnostics(at));
 968 
 969                 // corral any Snippets that need it
 970                 if (ins.stream().anyMatch(u -> u.corralIfNeeded(ins))) {
 971                     // if any were corralled, re-analyze everything
 972                     state.taskFactory.analyze(outerWrapSet(ins), cat -> {
 973                         ins.stream().forEach(u -> u.setCorralledDiagnostics(cat));
 974                         ins.stream().forEach(u -> u.setStatus(cat));
 975                         return null;
 976                     });
 977                 } else {
 978                     ins.stream().forEach(u -> u.setStatus(at));
 979                 }
 980                 return null;
 981             });
 982             // compile and load the legit snippets
 983             boolean success;
 984             while (true) {
 985                 List<Unit> legit = ins.stream()
 986                         .filter(Unit::isDefined)
 987                         .collect(toList());
 988                 state.debug(DBG_GEN, "compileAndLoad ins = %s -- legit = %s\n",
 989                         ins, legit);
 990                 if (legit.isEmpty()) {


1137             }
1138 
1139             @Override
1140             public long getEndPosition() {
1141                 return nameStart + name.length();
1142             }
1143 
1144             @Override
1145             public String getCode() {
1146                 return "jdk.eval.error.object.method";
1147             }
1148 
1149             @Override
1150             public String getMessage(Locale locale) {
1151                 return state.messageFormat("jshell.diag.object.method.fatal",
1152                         String.join(" ", objectMethods));
1153             }
1154         };
1155     }
1156 
1157     private DiagList modifierDiagnostics(ModifiersTree modtree,
1158             final TreeDissector dis, boolean isAbstractProhibited) {
1159 
1160         class ModifierDiagnostic extends Diag {
1161 
1162             final boolean fatal;
1163             final String message;
1164             long start;
1165             long end;
1166 
1167             ModifierDiagnostic(List<Modifier> list, boolean fatal) {



1168                 this.fatal = fatal;
1169                 StringBuilder sb = new StringBuilder();
1170                 for (Modifier mod : list) {
1171                     sb.append("'");
1172                     sb.append(mod.toString());
1173                     sb.append("' ");
1174                 }
1175                 String key = (list.size() > 1)
1176                         ? fatal
1177                             ? "jshell.diag.modifier.plural.fatal"
1178                             : "jshell.diag.modifier.plural.ignore"
1179                         : fatal
1180                             ? "jshell.diag.modifier.single.fatal"
1181                             : "jshell.diag.modifier.single.ignore";
1182                 this.message = state.messageFormat(key, sb.toString());
1183                 start = dis.getStartPosition(modtree);
1184                 end = dis.getEndPosition(modtree);
1185             }
1186 
1187             @Override
1188             public boolean isError() {
1189                 return fatal;
1190             }
1191 
1192             @Override
1193             public long getPosition() {
1194                 return start;
1195             }
1196 
1197             @Override
1198             public long getStartPosition() {
1199                 return start;
1200             }
1201 
1202             @Override
1203             public long getEndPosition() {
1204                 return end;
1205             }
1206 
1207             @Override
1208             public String getCode() {
1209                 return fatal
1210                         ? "jdk.eval.error.illegal.modifiers"
1211                         : "jdk.eval.warn.illegal.modifiers";
1212             }
1213 
1214             @Override
1215             public String getMessage(Locale locale) {
1216                 return message;
1217             }
1218         }



1219 
1220         List<Modifier> list = new ArrayList<>();
1221         boolean fatal = false;
1222         for (Modifier mod : modtree.getFlags()) {
1223             switch (mod) {
1224                 case SYNCHRONIZED:
1225                 case NATIVE:
1226                     list.add(mod);
1227                     fatal = true;
1228                     break;
1229                 case ABSTRACT:
1230                     if (isAbstractProhibited) {
1231                         list.add(mod);
1232                         fatal = true;
1233                     }
1234                     break;
1235                 case PUBLIC:
1236                 case PROTECTED:
1237                 case PRIVATE:
1238                     // quietly ignore, user cannot see effects one way or the other
1239                     break;
1240                 case STATIC:
1241                 case FINAL:
1242                     list.add(mod);
1243                     break;
1244             }
1245         }
1246         return list.isEmpty()
1247                 ? new DiagList()
1248                 : new DiagList(new ModifierDiagnostic(list, fatal));

















1249     }
1250 
1251     String computeDeclareName(TypeSymbol ts) {
1252         return Util.JSHELL_ANONYMOUS + "$" + Long.toUnsignedString(anonCount++);
1253     }
1254 }
< prev index next >