# HG changeset patch # User rfield # Date 1467409267 25200 # Node ID 437ba9bd258213d115a004d6c5f437c03086063b # Parent 0469f052203d87078fb8a24956d42dd23d9911d0 8159111: JShell API: Add access to wrappers and dependencies Reviewed-by: jlahoda diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -55,8 +55,9 @@ DeclarationSnippet(DeclarationKey key, String userSource, Wrap guts, String unitName, SubKind subkind, Wrap corralled, Collection declareReferences, - Collection bodyReferences) { - super(key, userSource, guts, unitName, subkind); + Collection bodyReferences, + DiagList syntheticDiags) { + super(key, userSource, guts, unitName, subkind, syntheticDiags); this.corralled = corralled; this.declareReferences = declareReferences; this.bodyReferences = bodyReferences; diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -28,8 +28,7 @@ import jdk.jshell.Key.ErroneousKey; /** - * A snippet of code that is not valid Java programming language code, and for - * which the kind of snippet could not be determined. + * A snippet of code that is not valid Java programming language code. * The Kind is {@link jdk.jshell.Snippet.Kind#ERRONEOUS ERRONEOUS}. *

* ErroneousSnippet is immutable: an access to @@ -38,7 +37,21 @@ */ public class ErroneousSnippet extends Snippet { - ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts, SubKind subkind) { - super(key, userSource, guts, null, subkind); + private final Kind probableKind; + + ErroneousSnippet(ErroneousKey key, String userSource, Wrap guts, + Kind probableKind, SubKind subkind) { + super(key, userSource, guts, null, subkind, null); + this.probableKind = probableKind; + } + + /** + * Returns what appears to be the intended Kind in this erroneous snippet. + * + * @return the probable Kind; or {@link Kind#ERRONEOUS} if that cannot be + * determined. + */ + public Kind probableKind() { + return probableKind; } } diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Fri Jul 01 14:41:07 2016 -0700 @@ -52,6 +52,7 @@ import jdk.jshell.Key.ErroneousKey; import jdk.jshell.Key.MethodKey; import jdk.jshell.Key.TypeDeclKey; +import jdk.jshell.Snippet.Kind; import jdk.jshell.Snippet.SubKind; import jdk.jshell.TaskFactory.AnalyzeTask; import jdk.jshell.TaskFactory.BaseTask; @@ -62,6 +63,7 @@ import jdk.jshell.Snippet.Status; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; +import static java.util.Collections.singletonList; import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; import static jdk.jshell.Util.DOIT_METHOD_NAME; import static jdk.jshell.Util.PREFIX_PATTERN; @@ -89,24 +91,75 @@ this.state = state; } + /** + * Evaluates a snippet of source. + * + * @param userSource the source of the snippet + * @return the list of primary and update events + * @throws IllegalStateException + */ List eval(String userSource) throws IllegalStateException { + List allEvents = new ArrayList<>(); + for (Snippet snip : sourceToSnippets(userSource)) { + if (snip.kind() == Kind.ERRONEOUS) { + state.maps.installSnippet(snip); + allEvents.add(new SnippetEvent( + snip, Status.NONEXISTENT, Status.REJECTED, + false, null, null, null)); + } else { + allEvents.addAll(declare(snip, snip.syntheticDiags())); + } + } + return allEvents; + } + + /** + * Converts the user source of a snippet into a Snippet list -- Snippet will + * have wrappers. + * + * @param userSource the source of the snippet + * @return usually a singleton list of Snippet, but may be empty or multiple + */ + List sourceToSnippetsWithWrappers(String userSource) { + List snippets = sourceToSnippets(userSource); + for (Snippet snip : snippets) { + if (snip.outerWrap() == null) { + snip.setOuterWrap( + (snip.kind() == Kind.IMPORT) + ? state.outerMap.wrapImport(snip.guts(), snip) + : state.outerMap.wrapInTrialClass(snip.guts()) + ); + } + } + return snippets; + } + + /** + * Converts the user source of a snippet into a Snippet object (or list of + * objects in the case of: int x, y, z;). Does not install the Snippets + * or execute them. + * + * @param userSource the source of the snippet + * @return usually a singleton list of Snippet, but may be empty or multiple + */ + private List sourceToSnippets(String userSource) { String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, false).cleared()); if (compileSource.length() == 0) { return Collections.emptyList(); } - // String folding messes up position information. ParseTask pt = state.taskFactory.new ParseTask(compileSource); + List units = pt.units(); + if (units.isEmpty()) { + return compileFailResult(pt, userSource, Kind.ERRONEOUS); + } + Tree unitTree = units.get(0); if (pt.getDiagnostics().hasOtherThanNotStatementErrors()) { - return compileFailResult(pt, userSource); + return compileFailResult(pt, userSource, kindOfTree(unitTree)); } - List units = pt.units(); - if (units.isEmpty()) { - return compileFailResult(pt, userSource); - } - // Erase illegal modifiers + // Erase illegal/ignored modifiers compileSource = new MaskCommentsAndModifiers(compileSource, true).cleared(); - Tree unitTree = units.get(0); + state.debug(DBG_GEN, "Kind: %s -- %s\n", unitTree.getKind(), unitTree); switch (unitTree.getKind()) { case IMPORT: @@ -130,7 +183,7 @@ } } - private List processImport(String userSource, String compileSource) { + private List processImport(String userSource, String compileSource) { Wrap guts = Wrap.simpleWrap(compileSource); Matcher mat = IMPORT_PATTERN.matcher(compileSource); String fullname; @@ -155,7 +208,7 @@ : (isStatic ? SINGLE_STATIC_IMPORT_SUBKIND : SINGLE_TYPE_IMPORT_SUBKIND); Snippet snip = new ImportSnippet(state.keyMap.keyForImport(keyName, snippetKind), userSource, guts, fullname, name, snippetKind, fullkey, isStatic, isStar); - return declare(snip); + return singletonList(snip); } private static class EvalPretty extends Pretty { @@ -187,8 +240,8 @@ } } - private List processVariables(String userSource, List units, String compileSource, ParseTask pt) { - List allEvents = new ArrayList<>(); + private List processVariables(String userSource, List units, String compileSource, ParseTask pt) { + List snippets = new ArrayList<>(); TreeDissector dis = TreeDissector.createByFirstClass(pt); for (Tree unitTree : units) { VariableTree vt = (VariableTree) unitTree; @@ -224,18 +277,16 @@ int nameEnd = nameStart + name.length(); Range rname = new Range(nameStart, nameEnd); Wrap guts = Wrap.varWrap(compileSource, rtype, sbBrackets.toString(), rname, rinit); + DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true); Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, name, subkind, typeName, - tds.declareReferences()); - DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true); - List res1 = declare(snip, modDiag); - allEvents.addAll(res1); + tds.declareReferences(), modDiag); + snippets.add(snip); } - - return allEvents; + return snippets; } - private List processExpression(String userSource, String compileSource) { + private List processExpression(String userSource, String compileSource) { String name = null; ExpressionInfo ei = typeOfExpression(compileSource); ExpressionTree assignVar; @@ -266,7 +317,7 @@ guts = Wrap.tempVarWrap(compileSource, typeName, name); Collection declareReferences = null; //TODO snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts, - name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences); + name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences, null); } else { guts = Wrap.methodReturnWrap(compileSource); snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts, @@ -282,15 +333,15 @@ at = trialCompile(guts); } if (at.hasErrors()) { - return compileFailResult(at, userSource); + return compileFailResult(at, userSource, Kind.EXPRESSION); } } snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); } - return declare(snip); + return singletonList(snip); } - private List processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) { + private List processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) { TreeDependencyScanner tds = new TreeDependencyScanner(); tds.scan(unitTree); @@ -306,11 +357,11 @@ Wrap guts = Wrap.classMemberWrap(compileSource); Snippet snip = new TypeDeclSnippet(key, userSource, guts, name, snippetKind, - corralled, tds.declareReferences(), tds.bodyReferences()); - return declare(snip, modDiag); + corralled, tds.declareReferences(), tds.bodyReferences(), modDiag); + return singletonList(snip); } - private List processStatement(String userSource, String compileSource) { + private List processStatement(String userSource, String compileSource) { Wrap guts = Wrap.methodWrap(compileSource); // Check for unreachable by trying AnalyzeTask at = trialCompile(guts); @@ -325,15 +376,15 @@ at = trialCompile(guts); } if (at.hasErrors()) { - return compileFailResult(at, userSource); + return compileFailResult(at, userSource, Kind.STATEMENT); } } } else { - return compileFailResult(at, userSource); + return compileFailResult(at, userSource, Kind.STATEMENT); } } Snippet snip = new StatementSnippet(state.keyMap.keyForStatement(), userSource, guts); - return declare(snip); + return singletonList(snip); } private AnalyzeTask trialCompile(Wrap guts) { @@ -341,7 +392,7 @@ return state.taskFactory.new AnalyzeTask(outer); } - private List processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) { + private List processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) { TreeDependencyScanner tds = new TreeDependencyScanner(); tds.scan(unitTree); TreeDissector dis = TreeDissector.createByFirstClass(pt); @@ -360,7 +411,7 @@ Wrap corralled = new Corraller(key.index(), pt.getContext()).corralMethod(mt); if (modDiag.hasErrors()) { - return compileFailResult(modDiag, userSource); + return compileFailResult(modDiag, userSource, Kind.METHOD); } Wrap guts = Wrap.classMemberWrap(compileSource); Range typeRange = dis.treeToRange(returnType); @@ -368,37 +419,76 @@ Snippet snip = new MethodSnippet(key, userSource, guts, name, signature, - corralled, tds.declareReferences(), tds.bodyReferences()); - return declare(snip, modDiag); + corralled, tds.declareReferences(), tds.bodyReferences(), modDiag); + return singletonList(snip); } - /** - * The snippet has failed, return with the rejected event - * - * @param xt the task from which to extract the failure diagnostics - * @param userSource the incoming bad user source - * @return a rejected snippet event - */ - private List compileFailResult(BaseTask xt, String userSource) { - return compileFailResult(xt.getDiagnostics(), userSource); + private Kind kindOfTree(Tree tree) { + switch (tree.getKind()) { + case IMPORT: + return Kind.IMPORT; + case VARIABLE: + return Kind.VAR; + case EXPRESSION_STATEMENT: + return Kind.EXPRESSION; + case CLASS: + case ENUM: + case ANNOTATION_TYPE: + case INTERFACE: + return Kind.TYPE_DECL; + case METHOD: + return Kind.METHOD; + default: + return Kind.STATEMENT; + } } /** - * The snippet has failed, return with the rejected event + * The snippet has failed, return with the rejected snippet + * + * @param xt the task from which to extract the failure diagnostics + * @param userSource the incoming bad user source + * @return a rejected snippet + */ + private List compileFailResult(BaseTask xt, String userSource, Kind probableKind) { + return compileFailResult(xt.getDiagnostics(), userSource, probableKind); + } + + /** + * The snippet has failed, return with the rejected snippet * * @param diags the failure diagnostics * @param userSource the incoming bad user source - * @return a rejected snippet event + * @return a rejected snippet */ - private List compileFailResult(DiagList diags, String userSource) { + private List compileFailResult(DiagList diags, String userSource, Kind probableKind) { ErroneousKey key = state.keyMap.keyForErroneous(); - Snippet snip = new ErroneousSnippet(key, userSource, null, SubKind.UNKNOWN_SUBKIND); + Snippet snip = new ErroneousSnippet(key, userSource, null, + probableKind, SubKind.UNKNOWN_SUBKIND); snip.setFailed(diags); - state.maps.installSnippet(snip); - return Collections.singletonList(new SnippetEvent( - snip, Status.NONEXISTENT, Status.REJECTED, - false, null, null, null) - ); + + // Install wrapper for query by SourceCodeAnalysis.wrapper + String compileSource = Util.trimEnd(new MaskCommentsAndModifiers(userSource, true).cleared()); + OuterWrap outer; + switch (probableKind) { + case IMPORT: + outer = state.outerMap.wrapImport(Wrap.simpleWrap(compileSource), snip); + break; + case EXPRESSION: + outer = state.outerMap.wrapInTrialClass(Wrap.methodReturnWrap(compileSource)); + break; + case VAR: + case TYPE_DECL: + case METHOD: + outer = state.outerMap.wrapInTrialClass(Wrap.classMemberWrap(compileSource)); + break; + default: + outer = state.outerMap.wrapInTrialClass(Wrap.methodWrap(compileSource)); + break; + } + snip.setOuterWrap(outer); + + return singletonList(snip); } private ExpressionInfo typeOfExpression(String expression) { @@ -430,10 +520,6 @@ return events(c, outs, null, null); } - private List declare(Snippet si) { - return declare(si, new DiagList()); - } - private List declare(Snippet si, DiagList generatedDiagnostics) { Unit c = new Unit(state, si, null, generatedDiagnostics); Set ins = new LinkedHashSet<>(); diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -39,7 +39,7 @@ public class ExpressionSnippet extends Snippet { ExpressionSnippet(ExpressionKey key, String userSource, Wrap guts, String name, SubKind subkind) { - super(key, userSource, guts, name, subkind); + super(key, userSource, guts, name, subkind, null); } /** diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -46,7 +46,7 @@ ImportSnippet(ImportKey key, String userSource, Wrap guts, String fullname, String name, SubKind subkind, String fullkey, boolean isStatic, boolean isStar) { - super(key, userSource, guts, name, subkind); + super(key, userSource, guts, name, subkind, null); this.fullname = fullname; this.fullkey = fullkey; this.isStatic = isStatic; diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Fri Jul 01 14:41:07 2016 -0700 @@ -377,7 +377,9 @@ * Evaluate the input String, including definition and/or execution, if * applicable. The input is checked for errors, unless the errors can be * deferred (as is the case with some unresolvedDependencies references), - * errors will abort evaluation. The input should be + * errors will abort evaluation. + *

+ * The input should be * exactly one complete snippet of source code, that is, one expression, * statement, variable declaration, method declaration, class declaration, * or import. diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -44,8 +44,10 @@ MethodSnippet(MethodKey key, String userSource, Wrap guts, String name, String signature, Wrap corralled, - Collection declareReferences, Collection bodyReferences) { - super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, declareReferences, bodyReferences); + Collection declareReferences, Collection bodyReferences, + DiagList syntheticDiags) { + super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, + declareReferences, bodyReferences, syntheticDiags); this.signature = signature; } diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/OuterImportSnippetWrap.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/OuterImportSnippetWrap.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/OuterImportSnippetWrap.java Fri Jul 01 14:41:07 2016 -0700 @@ -25,8 +25,6 @@ package jdk.jshell; -import java.util.IdentityHashMap; -import java.util.List; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -37,8 +37,9 @@ */ public abstract class PersistentSnippet extends Snippet { - PersistentSnippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) { - super(key, userSource, guts, unitName, subkind); + PersistentSnippet(Key key, String userSource, Wrap guts, String unitName, + SubKind subkind, DiagList syntheticDiags) { + super(key, userSource, guts, unitName, subkind, syntheticDiags); } /** diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -563,13 +563,18 @@ private Status status; private List unresolved; private DiagList diagnostics; + private final DiagList syntheticDiags; - Snippet(Key key, String userSource, Wrap guts, String unitName, SubKind subkind) { + Snippet(Key key, String userSource, Wrap guts, String unitName, + SubKind subkind, DiagList syntheticDiags) { this.key = key; this.source = userSource; this.guts = guts; this.unitName = unitName; this.subkind = subkind; + this.syntheticDiags = syntheticDiags==null + ? new DiagList() + : syntheticDiags; this.status = Status.NONEXISTENT; setSequenceNumber(0); } @@ -644,6 +649,10 @@ return diagnostics; } + DiagList syntheticDiags() { + return syntheticDiags; + } + /** * @return the corralled guts */ diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java Fri Jul 01 14:41:07 2016 -0700 @@ -25,6 +25,7 @@ package jdk.jshell; +import java.util.Collection; import java.util.List; /** @@ -92,6 +93,51 @@ public abstract QualifiedNames listQualifiedNames(String code, int cursor); /** + * Returns the wrapper information for the {@code Snippet}. The wrapper changes as + * the environment changes, so calls to this method at different times may + * yield different results. + * + * @param snippet the {@code Snippet} from which to retrieve the wrapper + * @return information on the wrapper + */ + public abstract SnippetWrapper wrapper(Snippet snippet); + + /** + * Returns the wrapper information for the snippet within the + * input source string. + *

+ * Wrapper information for malformed and incomplete + * snippets also generate wrappers. The list is in snippet encounter + * order. The wrapper changes as the environment changes, so calls to this + * method at different times may yield different results. + *

+ * The input should be + * exactly one complete snippet of source code, that is, one expression, + * statement, variable declaration, method declaration, class declaration, + * or import. + * To break arbitrary input into individual complete snippets, use + * {@link SourceCodeAnalysis#analyzeCompletion(String)}. + *

+ * The wrapper may not match that returned by + * {@link SourceCodeAnalysis#wrapper(Snippet) wrapper(Snippet)}, + * were the source converted to a {@code Snippet}. + * + * @param input the source input from which to generate wrappers + * @return a list of wrapper information + */ + public abstract List wrappers(String input); + + /** + * Returns a collection of {@code Snippet}s which might need updating if the + * given {@code Snippet} is updated. The returned collection is designed to + * be inclusive and may include many false positives. + * + * @param snippet the {@code Snippet} whose dependents are requested + * @return the collection of dependents + */ + public abstract Collection dependents(Snippet snippet); + + /** * Internal only constructor */ SourceCodeAnalysis() {} @@ -302,7 +348,7 @@ } /** - * Indicates whether the result is based on up to date data. The + * Indicates whether the result is based on up-to-date data. The * {@link SourceCodeAnalysis#listQualifiedNames(java.lang.String, int) listQualifiedNames} * method may return before the classpath is fully inspected, in which case this method will * return {@code false}. If the result is based on a fully inspected classpath, this method @@ -327,4 +373,83 @@ } } + + /** + * The wrapping of a snippet of Java source into valid top-level Java + * source. The wrapping will always either be an import or include a + * synthetic class at the top-level. If a synthetic class is generated, it + * will be proceeded by the package and import declarations, and may contain + * synthetic class members. + *

+ * This interface, in addition to the mapped form, provides the context and + * position mapping information. + */ + public interface SnippetWrapper { + + /** + * Returns the input that is wrapped. For + * {@link SourceCodeAnalysis#wrappers(java.lang.String) wrappers(String)}, + * this is the source of the snippet within the input. A variable + * declaration of {@code N} variables will map to {@code N} wrappers + * with the source separated. + *

+ * For {@link SourceCodeAnalysis#wrapper(Snippet) wrapper(Snippet)}, + * this is {@link Snippet#source() }. + * + * @return the input source corresponding to the wrapper. + */ + String source(); + + /** + * Returns a Java class definition that wraps the + * {@link SnippetWrapper#source()} or, if an import, the import source. + *

+ * If the input is not a valid Snippet, this will not be a valid + * class/import definition. + *

+ * The source may be divided and mapped to different locations within + * the wrapped source. + * + * @return the source wrapped into top-level Java code + */ + String wrapped(); + + /** + * Returns the fully qualified class name of the + * {@link SnippetWrapper#wrapped() } class. + * For erroneous input, a best guess is returned. + * + * @return the name of the synthetic wrapped class; if an import, the + * name is not defined + */ + String fullClassName(); + + /** + * Returns the {@link Snippet.Kind} of the + * {@link SnippetWrapper#source()}. + * + * @return an enum representing the general kind of snippet. + */ + Snippet.Kind kind(); + + /** + * Maps character position within the source to character position + * within the wrapped. + * + * @param pos the position in {@link SnippetWrapper#source()} + * @return the corresponding position in + * {@link SnippetWrapper#wrapped() } + */ + int sourceToWrappedPosition(int pos); + + /** + * Maps character position within the wrapped to character position + * within the source. + * + * @param pos the position in {@link SnippetWrapper#wrapped()} + * @return the corresponding position in + * {@link SnippetWrapper#source() } + */ + int wrappedToSourcePosition(int pos); + } } diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Fri Jul 01 14:41:07 2016 -0700 @@ -413,6 +413,55 @@ return result; } + @Override + public SnippetWrapper wrapper(Snippet snippet) { + return new SnippetWrapper() { + @Override + public String source() { + return snippet.source(); + } + + @Override + public String wrapped() { + return snippet.outerWrap().wrapped(); + } + + @Override + public String fullClassName() { + return snippet.classFullName(); + } + + @Override + public Snippet.Kind kind() { + return snippet.kind() == Snippet.Kind.ERRONEOUS + ? ((ErroneousSnippet) snippet).probableKind() + : snippet.kind(); + } + + @Override + public int sourceToWrappedPosition(int pos) { + return snippet.outerWrap().snippetIndexToWrapIndex(pos); + } + + @Override + public int wrappedToSourcePosition(int pos) { + return snippet.outerWrap().wrapIndexToSnippetIndex(pos); + } + }; + } + + @Override + public List wrappers(String input) { + return proc.eval.sourceToSnippetsWithWrappers(input).stream() + .map(sn -> wrapper(sn)) + .collect(toList()); + } + + @Override + public Collection dependents(Snippet snippet) { + return proc.maps.getDependents(snippet); + } + private boolean isStaticContext(AnalyzeTask at, TreePath path) { switch (path.getLeaf().getKind()) { case ARRAY_TYPE: diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -39,6 +39,6 @@ public class StatementSnippet extends Snippet { StatementSnippet(StatementKey key, String userSource, Wrap guts) { - super(key, userSource, guts, null, SubKind.STATEMENT_SUBKIND); + super(key, userSource, guts, null, SubKind.STATEMENT_SUBKIND, null); } } diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -42,8 +42,10 @@ TypeDeclSnippet(TypeDeclKey key, String userSource, Wrap guts, String unitName, SubKind subkind, Wrap corralled, Collection declareReferences, - Collection bodyReferences) { - super(key, userSource, guts, unitName, subkind, corralled, declareReferences, bodyReferences); + Collection bodyReferences, + DiagList syntheticDiags) { + super(key, userSource, guts, unitName, subkind, corralled, + declareReferences, bodyReferences, syntheticDiags); } /**** internal access ****/ diff -r 0469f052203d -r 437ba9bd2582 langtools/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java Fri Jul 01 14:16:28 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java Fri Jul 01 14:41:07 2016 -0700 @@ -43,8 +43,10 @@ VarSnippet(VarKey key, String userSource, Wrap guts, String name, SubKind subkind, String typeName, - Collection declareReferences) { - super(key, userSource, guts, name, subkind, null, declareReferences, null); + Collection declareReferences, + DiagList syntheticDiags) { + super(key, userSource, guts, name, subkind, null, declareReferences, + null, syntheticDiags); this.typeName = typeName; } diff -r 0469f052203d -r 437ba9bd2582 langtools/test/jdk/jshell/WrapperTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/jshell/WrapperTest.java Fri Jul 01 14:41:07 2016 -0700 @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., start1 Franklin St, Fifth Floor, Boston, MA 02110-1length01 USA. + * + * Please contact Oracle, start00 Oracle Parkway, Redwood Shores, CA 9406start USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8159111 + * @summary test wrappers and dependencies + * @modules jdk.jshell/jdk.jshell + * @build KullaTesting + * @run testng WrapperTest + */ + +import java.util.Collection; +import java.util.List; +import org.testng.annotations.Test; +import jdk.jshell.ErroneousSnippet; +import jdk.jshell.Snippet; +import jdk.jshell.Snippet.Kind; +import jdk.jshell.SourceCodeAnalysis.SnippetWrapper; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED; +import static jdk.jshell.Snippet.Status.VALID; + +@Test +public class WrapperTest extends KullaTesting { + + public void testMethod() { + String src = "void glib() { System.out.println(\"hello\"); }"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.METHOD, "void", "glib", "println"); + assertPosition(swl.get(0), src, 0, 4); + assertPosition(swl.get(0), src, 5, 4); + assertPosition(swl.get(0), src, 15, 6); + + Snippet g = methodKey(assertEval(src, added(VALID))); + SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g); + assertWrapperHas(swg, src, Kind.METHOD, "void", "glib", "println"); + assertPosition(swg, src, 0, 4); + assertPosition(swg, src, 5, 4); + assertPosition(swg, src, 15, 6); + } + + @Test(enabled = false) // TODO 8159740 + public void testMethodCorralled() { + String src = "void glib() { f(); }"; + Snippet g = methodKey(assertEval(src, added(RECOVERABLE_DEFINED))); + SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g); + assertWrapperHas(swg, src, Kind.METHOD, "void", "glib"); + assertPosition(swg, src, 5, 4); + } + + public void testMethodBad() { + String src = "void flob() { ?????; }"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.METHOD, "void", "flob", "?????"); + assertPosition(swl.get(0), src, 9, 2); + + Snippet f = key(assertEvalFail(src)); + assertEquals(f.kind(), Kind.ERRONEOUS); + assertEquals(((ErroneousSnippet)f).probableKind(), Kind.METHOD); + SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f); + assertWrapperHas(sw, src, Kind.METHOD, "void", "flob", "?????"); + assertPosition(swl.get(0), src, 14, 5); + } + + public void testVar() { + String src = "int gx = 1234;"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.VAR, "int", "gx", "1234"); + assertPosition(swl.get(0), src, 4, 2); + + Snippet g = varKey(assertEval(src, added(VALID))); + SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g); + assertWrapperHas(swg, src, Kind.VAR, "int", "gx", "1234"); + assertPosition(swg, src, 0, 3); + } + + public void testVarBad() { + String src = "double dd = ?????;"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.VAR, "double", "dd", "?????"); + assertPosition(swl.get(0), src, 9, 2); + + Snippet f = key(assertEvalFail(src)); + assertEquals(f.kind(), Kind.ERRONEOUS); + assertEquals(((ErroneousSnippet)f).probableKind(), Kind.VAR); + SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f); + assertWrapperHas(sw, src, Kind.VAR, "double", "dd", "?????"); + assertPosition(swl.get(0), src, 12, 5); + } + + public void testImport() { + String src = "import java.lang.*;"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.IMPORT, "import", "java.lang"); + assertPosition(swl.get(0), src, 7, 4); + + Snippet g = key(assertEval(src, added(VALID))); + SnippetWrapper swg = getState().sourceCodeAnalysis().wrapper(g); + assertWrapperHas(swg, src, Kind.IMPORT, "import", "java.lang"); + assertPosition(swg, src, 0, 6); + } + + public void testImportBad() { + String src = "import java.?????;"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.IMPORT, "import", "?????"); + assertPosition(swl.get(0), src, 7, 4); + + Snippet f = key(assertEvalFail(src)); + assertEquals(f.kind(), Kind.ERRONEOUS); + assertEquals(((ErroneousSnippet)f).probableKind(), Kind.IMPORT); + SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f); + assertWrapperHas(sw, src, Kind.IMPORT, "import", "?????"); + assertPosition(swl.get(0), src, 0, 6); + } + + public void testErroneous() { + String src = "@@@@@@@@@@"; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 1, "unexpected list length"); + assertWrapperHas(swl.get(0), src, Kind.ERRONEOUS, "@@@@@@@@@@"); + assertPosition(swl.get(0), src, 0, 10); + + Snippet f = key(assertEvalFail(src)); + assertEquals(f.kind(), Kind.ERRONEOUS); + assertEquals(((ErroneousSnippet)f).probableKind(), Kind.ERRONEOUS); + SnippetWrapper sw = getState().sourceCodeAnalysis().wrapper(f); + assertWrapperHas(sw, src, Kind.ERRONEOUS, "@@@@@@@@@@"); + assertPosition(swl.get(0), src, 0, 10); + } + + public void testEmpty() { + String src = ""; + List swl = getState().sourceCodeAnalysis().wrappers(src); + assertEquals(swl.size(), 0, "expected empty list"); + } + + public void testDependencies() { + Snippet a = key(assertEval("int aaa = 6;", added(VALID))); + Snippet b = key(assertEval("class B { B(int x) { aaa = x; } }", added(VALID))); + Snippet c = key(assertEval("B ccc() { return new B(aaa); }", added(VALID))); + Collection dep; + dep = getState().sourceCodeAnalysis().dependents(c); + assertEquals(dep.size(), 0); + dep = getState().sourceCodeAnalysis().dependents(b); + assertEquals(dep.size(), 1); + assertTrue(dep.contains(c)); + dep = getState().sourceCodeAnalysis().dependents(a); + assertEquals(dep.size(), 2); + assertTrue(dep.contains(c)); + assertTrue(dep.contains(b)); + } + + private void assertWrapperHas(SnippetWrapper sw, String source, Kind kind, String... has) { + assertEquals(sw.source(), source); + assertEquals(sw.kind(), kind); + if (kind == Kind.IMPORT) { + assertTrue(sw.wrapped().contains("import")); + } else { + String cn = sw.fullClassName(); + int idx = cn.lastIndexOf("."); + assertTrue(sw.wrapped().contains(cn.substring(idx+1))); + assertTrue(sw.wrapped().contains("class")); + } + for (String s : has) { + assertTrue(sw.wrapped().contains(s)); + } + } + + private void assertPosition(SnippetWrapper sw, String source, int start, int length) { + int wpg = sw.sourceToWrappedPosition(start); + assertEquals(sw.wrapped().substring(wpg, wpg+length), + source.substring(start, start+length), + "position " + wpg + " in " + sw.wrapped()); + assertEquals(sw.wrappedToSourcePosition(wpg), start); + } +}