langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
changeset 37939 3eb8c2a89b77
parent 37644 33cf53901cac
child 37944 1153fab98d25
equal deleted inserted replaced
37938:42baa89d2156 37939:3eb8c2a89b77
    38 import com.sun.source.tree.NewClassTree;
    38 import com.sun.source.tree.NewClassTree;
    39 import com.sun.source.tree.Scope;
    39 import com.sun.source.tree.Scope;
    40 import com.sun.source.tree.Tree;
    40 import com.sun.source.tree.Tree;
    41 import com.sun.source.tree.Tree.Kind;
    41 import com.sun.source.tree.Tree.Kind;
    42 import com.sun.source.tree.VariableTree;
    42 import com.sun.source.tree.VariableTree;
       
    43 import com.sun.source.util.JavacTask;
    43 import com.sun.source.util.SourcePositions;
    44 import com.sun.source.util.SourcePositions;
    44 import com.sun.source.util.TreePath;
    45 import com.sun.source.util.TreePath;
    45 import com.sun.source.util.TreePathScanner;
    46 import com.sun.source.util.TreePathScanner;
       
    47 import com.sun.source.util.Trees;
    46 import com.sun.tools.javac.api.JavacScope;
    48 import com.sun.tools.javac.api.JavacScope;
       
    49 import com.sun.tools.javac.api.JavacTaskImpl;
    47 import com.sun.tools.javac.code.Flags;
    50 import com.sun.tools.javac.code.Flags;
    48 import com.sun.tools.javac.code.Symbol.CompletionFailure;
    51 import com.sun.tools.javac.code.Symbol.CompletionFailure;
    49 import com.sun.tools.javac.code.Symbol.VarSymbol;
    52 import com.sun.tools.javac.code.Symbol.VarSymbol;
    50 import com.sun.tools.javac.code.Symtab;
    53 import com.sun.tools.javac.code.Symtab;
    51 import com.sun.tools.javac.code.Type;
    54 import com.sun.tools.javac.code.Type;
   117 import javax.lang.model.type.ArrayType;
   120 import javax.lang.model.type.ArrayType;
   118 import javax.lang.model.type.ExecutableType;
   121 import javax.lang.model.type.ExecutableType;
   119 import javax.lang.model.type.TypeKind;
   122 import javax.lang.model.type.TypeKind;
   120 import javax.lang.model.util.ElementFilter;
   123 import javax.lang.model.util.ElementFilter;
   121 import javax.lang.model.util.Types;
   124 import javax.lang.model.util.Types;
       
   125 import javax.tools.JavaCompiler;
   122 import javax.tools.JavaFileManager.Location;
   126 import javax.tools.JavaFileManager.Location;
       
   127 import javax.tools.JavaFileObject;
       
   128 import javax.tools.StandardJavaFileManager;
   123 import javax.tools.StandardLocation;
   129 import javax.tools.StandardLocation;
       
   130 import javax.tools.ToolProvider;
   124 
   131 
   125 import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   132 import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   126 
   133 
   127 /**
   134 /**
   128  * The concrete implementation of SourceCodeAnalysis.
   135  * The concrete implementation of SourceCodeAnalysis.
   930         } finally {
   937         } finally {
   931             resumeIndexing();
   938             resumeIndexing();
   932         }
   939         }
   933     }
   940     }
   934 
   941 
       
   942     //tweaked by tests to disable reading parameter names from classfiles so that tests using
       
   943     //JDK's classes are stable for both release and fastdebug builds:
       
   944     private final String[] keepParameterNames = new String[] {
       
   945         "-XDsave-parameter-names=true"
       
   946     };
       
   947 
   935     private String documentationImpl(String code, int cursor) {
   948     private String documentationImpl(String code, int cursor) {
   936         code = code.substring(0, cursor);
   949         code = code.substring(0, cursor);
   937         if (code.trim().isEmpty()) { //TODO: comment handling
   950         if (code.trim().isEmpty()) { //TODO: comment handling
   938             code += ";";
   951             code += ";";
   939         }
   952         }
   940 
   953 
   941         if (guessKind(code) == Kind.IMPORT)
   954         if (guessKind(code) == Kind.IMPORT)
   942             return null;
   955             return null;
   943 
   956 
   944         OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
   957         OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
   945         AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap);
   958         AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
   946         SourcePositions sp = at.trees().getSourcePositions();
   959         SourcePositions sp = at.trees().getSourcePositions();
   947         CompilationUnitTree topLevel = at.firstCuTree();
   960         CompilationUnitTree topLevel = at.firstCuTree();
   948         TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
   961         TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
   949 
   962 
   950         if (tp == null)
   963         if (tp == null)
   981                         .stream()
   994                         .stream()
   982                         .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
   995                         .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
   983                         .collect(Collectors.toList());
   996                         .collect(Collectors.toList());
   984         }
   997         }
   985 
   998 
   986         return Util.stream(candidates)
   999         try (SourceCache sourceCache = new SourceCache(at)) {
   987                 .map(method -> Util.expunge(element2String(method.fst)))
  1000             return Util.stream(candidates)
   988                 .collect(joining("\n"));
  1001                     .map(method -> Util.expunge(element2String(sourceCache, method.fst)))
       
  1002                     .collect(joining("\n"));
       
  1003         }
   989     }
  1004     }
   990 
  1005 
   991     private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
  1006     private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
   992         if (arguments.size() == 1) {
  1007         if (arguments.size() == 1) {
   993             Tree firstArgument = arguments.get(0);
  1008             Tree firstArgument = arguments.get(0);
   994             return firstArgument.getKind() == Kind.ERRONEOUS;
  1009             return firstArgument.getKind() == Kind.ERRONEOUS;
   995         }
  1010         }
   996         return false;
  1011         return false;
   997     }
  1012     }
   998 
  1013 
   999     private String element2String(Element el) {
  1014     private String element2String(SourceCache sourceCache, Element el) {
       
  1015         try {
       
  1016             if (hasSyntheticParameterNames(el)) {
       
  1017                 el = sourceCache.getSourceMethod(el);
       
  1018             }
       
  1019         } catch (IOException ex) {
       
  1020             proc.debug(ex, "SourceCodeAnalysisImpl.element2String(..., " + el + ")");
       
  1021         }
       
  1022 
       
  1023         return Util.expunge(elementHeader(el));
       
  1024     }
       
  1025 
       
  1026     private boolean hasSyntheticParameterNames(Element el) {
       
  1027         if (el.getKind() != ElementKind.CONSTRUCTOR && el.getKind() != ElementKind.METHOD)
       
  1028             return false;
       
  1029 
       
  1030         ExecutableElement ee = (ExecutableElement) el;
       
  1031 
       
  1032         if (ee.getParameters().isEmpty())
       
  1033             return false;
       
  1034 
       
  1035         return ee.getParameters()
       
  1036                  .stream()
       
  1037                  .allMatch(param -> param.getSimpleName().toString().startsWith("arg"));
       
  1038     }
       
  1039 
       
  1040     private final class SourceCache implements AutoCloseable {
       
  1041         private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
       
  1042         private final Map<String, Map<String, Element>> topLevelName2Signature2Method = new HashMap<>();
       
  1043         private final AnalyzeTask originalTask;
       
  1044         private final StandardJavaFileManager fm;
       
  1045 
       
  1046         public SourceCache(AnalyzeTask originalTask) {
       
  1047             this.originalTask = originalTask;
       
  1048             Iterable<? extends Path> sources = findSources();
       
  1049             if (sources.iterator().hasNext()) {
       
  1050                 StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
       
  1051                 try {
       
  1052                     fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sources);
       
  1053                 } catch (IOException ex) {
       
  1054                     proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.<init>(...)");
       
  1055                     fm = null;
       
  1056                 }
       
  1057                 this.fm = fm;
       
  1058             } else {
       
  1059                 //don't waste time if there are no sources
       
  1060                 this.fm = null;
       
  1061             }
       
  1062         }
       
  1063 
       
  1064         public Element getSourceMethod(Element method) throws IOException {
       
  1065             if (fm == null)
       
  1066                 return method;
       
  1067 
       
  1068             TypeElement type = topLevelType(method);
       
  1069 
       
  1070             if (type == null)
       
  1071                 return method;
       
  1072 
       
  1073             String binaryName = originalTask.task.getElements().getBinaryName(type).toString();
       
  1074 
       
  1075             Map<String, Element> cache = topLevelName2Signature2Method.get(binaryName);
       
  1076 
       
  1077             if (cache == null) {
       
  1078                 topLevelName2Signature2Method.put(binaryName, cache = createMethodCache(binaryName));
       
  1079             }
       
  1080 
       
  1081             String handle = elementHeader(method, false);
       
  1082 
       
  1083             return cache.getOrDefault(handle, method);
       
  1084         }
       
  1085 
       
  1086         private TypeElement topLevelType(Element el) {
       
  1087             while (el != null && el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
       
  1088                 el = el.getEnclosingElement();
       
  1089             }
       
  1090 
       
  1091             return el != null && (el.getKind().isClass() || el.getKind().isInterface()) ? (TypeElement) el : null;
       
  1092         }
       
  1093 
       
  1094         private Map<String, Element> createMethodCache(String binaryName) throws IOException {
       
  1095             Pair<JavacTask, CompilationUnitTree> source = findSource(binaryName);
       
  1096 
       
  1097             if (source == null)
       
  1098                 return Collections.emptyMap();
       
  1099 
       
  1100             Map<String, Element> signature2Method = new HashMap<>();
       
  1101             Trees trees = Trees.instance(source.fst);
       
  1102 
       
  1103             new TreePathScanner<Void, Void>() {
       
  1104                 @Override @DefinedBy(Api.COMPILER_TREE)
       
  1105                 public Void visitMethod(MethodTree node, Void p) {
       
  1106                     Element currentMethod = trees.getElement(getCurrentPath());
       
  1107 
       
  1108                     if (currentMethod != null) {
       
  1109                         signature2Method.put(elementHeader(currentMethod, false), currentMethod);
       
  1110                     }
       
  1111 
       
  1112                     return null;
       
  1113                 }
       
  1114             }.scan(source.snd, null);
       
  1115 
       
  1116             return signature2Method;
       
  1117         }
       
  1118 
       
  1119         private Pair<JavacTask, CompilationUnitTree> findSource(String binaryName) throws IOException {
       
  1120             JavaFileObject jfo = fm.getJavaFileForInput(StandardLocation.SOURCE_PATH,
       
  1121                                                         binaryName,
       
  1122                                                         JavaFileObject.Kind.SOURCE);
       
  1123 
       
  1124             if (jfo == null)
       
  1125                 return null;
       
  1126 
       
  1127             List<JavaFileObject> jfos = Arrays.asList(jfo);
       
  1128             JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, d -> {}, null, null, jfos);
       
  1129             Iterable<? extends CompilationUnitTree> cuts = task.parse();
       
  1130 
       
  1131             task.enter();
       
  1132 
       
  1133             return Pair.of(task, cuts.iterator().next());
       
  1134         }
       
  1135 
       
  1136         @Override
       
  1137         public void close() {
       
  1138             try {
       
  1139                 if (fm != null) {
       
  1140                     fm.close();
       
  1141                 }
       
  1142             } catch (IOException ex) {
       
  1143                 proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.close()");
       
  1144             }
       
  1145         }
       
  1146     }
       
  1147 
       
  1148     private Iterable<? extends Path> availableSources;
       
  1149 
       
  1150     private Iterable<? extends Path> findSources() {
       
  1151         if (availableSources != null) {
       
  1152             return availableSources;
       
  1153         }
       
  1154         List<Path> result = new ArrayList<>();
       
  1155         Path home = Paths.get(System.getProperty("java.home"));
       
  1156         Path srcZip = home.resolve("src.zip");
       
  1157         if (!Files.isReadable(srcZip))
       
  1158             srcZip = home.getParent().resolve("src.zip");
       
  1159         if (Files.isReadable(srcZip))
       
  1160             result.add(srcZip);
       
  1161         return availableSources = result;
       
  1162     }
       
  1163 
       
  1164     private String elementHeader(Element el) {
       
  1165         return elementHeader(el, true);
       
  1166     }
       
  1167 
       
  1168     private String elementHeader(Element el, boolean includeParameterNames) {
  1000         switch (el.getKind()) {
  1169         switch (el.getKind()) {
  1001             case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
  1170             case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
  1002                 return ((TypeElement) el).getQualifiedName().toString();
  1171                 return ((TypeElement) el).getQualifiedName().toString();
  1003             case FIELD:
  1172             case FIELD:
  1004                 return element2String(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
  1173                 return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
  1005             case ENUM_CONSTANT:
  1174             case ENUM_CONSTANT:
  1006                 return element2String(el.getEnclosingElement()) + "." + el.getSimpleName();
  1175                 return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName();
  1007             case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
  1176             case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
  1008                 return el.getSimpleName() + ":" + el.asType();
  1177                 return el.getSimpleName() + ":" + el.asType();
  1009             case CONSTRUCTOR: case METHOD:
  1178             case CONSTRUCTOR: case METHOD:
  1010                 StringBuilder header = new StringBuilder();
  1179                 StringBuilder header = new StringBuilder();
  1011                 header.append(element2String(el.getEnclosingElement()));
  1180                 header.append(elementHeader(el.getEnclosingElement()));
  1012                 if (el.getKind() == ElementKind.METHOD) {
  1181                 if (el.getKind() == ElementKind.METHOD) {
  1013                     header.append(".");
  1182                     header.append(".");
  1014                     header.append(el.getSimpleName());
  1183                     header.append(el.getSimpleName());
  1015                 }
  1184                 }
  1016                 header.append("(");
  1185                 header.append("(");
  1024                         header.append("...");
  1193                         header.append("...");
  1025 
  1194 
  1026                     } else {
  1195                     } else {
  1027                         header.append(p.asType());
  1196                         header.append(p.asType());
  1028                     }
  1197                     }
  1029                     header.append(" ");
  1198                     if (includeParameterNames) {
  1030                     header.append(p.getSimpleName());
  1199                         header.append(" ");
       
  1200                         header.append(p.getSimpleName());
       
  1201                     }
  1031                     sep = ", ";
  1202                     sep = ", ";
  1032                 }
  1203                 }
  1033                 header.append(")");
  1204                 header.append(")");
  1034                 return header.toString();
  1205                 return header.toString();
  1035            default:
  1206            default: