8159111: JShell API: Add access to wrappers and dependencies
authorrfield
Fri, 01 Jul 2016 14:41:07 -0700
changeset 39370 437ba9bd2582
parent 39369 0469f052203d
child 39371 ac04aefa4ba6
8159111: JShell API: Add access to wrappers and dependencies Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/jshell/DeclarationSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/ErroneousSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/ExpressionSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/ImportSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/MethodSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/OuterImportSnippetWrap.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/PersistentSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/Snippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysis.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/StatementSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/TypeDeclSnippet.java
langtools/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java
langtools/test/jdk/jshell/WrapperTest.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<String> declareReferences,
-            Collection<String> bodyReferences) {
-        super(key, userSource, guts, unitName, subkind);
+            Collection<String> bodyReferences,
+            DiagList syntheticDiags) {
+        super(key, userSource, guts, unitName, subkind, syntheticDiags);
         this.corralled = corralled;
         this.declareReferences = declareReferences;
         this.bodyReferences = bodyReferences;
--- 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}.
  * <p>
  * <code>ErroneousSnippet</code> 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;
     }
 }
--- 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<SnippetEvent> eval(String userSource) throws IllegalStateException {
+        List<SnippetEvent> 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<Snippet> sourceToSnippetsWithWrappers(String userSource) {
+        List<Snippet> 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<Snippet> 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<? extends Tree> 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<? extends Tree> 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<SnippetEvent> processImport(String userSource, String compileSource) {
+    private List<Snippet> 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<SnippetEvent> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
-        List<SnippetEvent> allEvents = new ArrayList<>();
+    private List<Snippet> processVariables(String userSource, List<? extends Tree> units, String compileSource, ParseTask pt) {
+        List<Snippet> 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<SnippetEvent> res1 = declare(snip, modDiag);
-            allEvents.addAll(res1);
+                    tds.declareReferences(), modDiag);
+            snippets.add(snip);
         }
-
-        return allEvents;
+        return snippets;
     }
 
-    private List<SnippetEvent> processExpression(String userSource, String compileSource) {
+    private List<Snippet> 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<String> 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<SnippetEvent> processClass(String userSource, Tree unitTree, String compileSource, SubKind snippetKind, ParseTask pt) {
+    private List<Snippet> 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<SnippetEvent> processStatement(String userSource, String compileSource) {
+    private List<Snippet> 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<SnippetEvent> processMethod(String userSource, Tree unitTree, String compileSource, ParseTask pt) {
+    private List<Snippet> 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<SnippetEvent> 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<Snippet> 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<SnippetEvent> compileFailResult(DiagList diags, String userSource) {
+    private List<Snippet> 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<SnippetEvent> declare(Snippet si) {
-        return declare(si, new DiagList());
-    }
-
     private List<SnippetEvent> declare(Snippet si, DiagList generatedDiagnostics) {
         Unit c = new Unit(state, si, null, generatedDiagnostics);
         Set<Unit> ins = new LinkedHashSet<>();
--- 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);
     }
 
     /**
--- 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;
--- 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.
+     * <p>
+     * The input should be
      * exactly one complete snippet of source code, that is, one expression,
      * statement, variable declaration, method declaration, class declaration,
      * or import.
--- 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<String> declareReferences, Collection<String> bodyReferences) {
-        super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled, declareReferences, bodyReferences);
+            Collection<String> declareReferences, Collection<String> bodyReferences,
+            DiagList syntheticDiags) {
+        super(key, userSource, guts, name, SubKind.METHOD_SUBKIND, corralled,
+                declareReferences, bodyReferences, syntheticDiags);
         this.signature = signature;
     }
 
--- 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;
 
--- 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);
     }
 
     /**
--- 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<String> 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
      */
--- 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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)}.
+     * <p>
+     * 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<SnippetWrapper> 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<Snippet> 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.
+     * <p>
+     * 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.
+         * <p>
+         * 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.
+         * <p>
+         * If the input is not a valid Snippet, this will not be a valid
+         * class/import definition.
+         * <p>
+         * 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);
+    }
 }
--- 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<SnippetWrapper> wrappers(String input) {
+        return proc.eval.sourceToSnippetsWithWrappers(input).stream()
+                .map(sn -> wrapper(sn))
+                .collect(toList());
+    }
+
+    @Override
+    public Collection<Snippet> dependents(Snippet snippet) {
+        return proc.maps.getDependents(snippet);
+    }
+
     private boolean isStaticContext(AnalyzeTask at, TreePath path) {
         switch (path.getLeaf().getKind()) {
             case ARRAY_TYPE:
--- 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);
     }
 }
--- 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<String> declareReferences,
-            Collection<String> bodyReferences) {
-        super(key, userSource, guts, unitName, subkind, corralled, declareReferences, bodyReferences);
+            Collection<String> bodyReferences,
+            DiagList syntheticDiags) {
+        super(key, userSource, guts, unitName, subkind, corralled,
+                declareReferences, bodyReferences, syntheticDiags);
     }
 
     /**** internal access ****/
--- 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<String> declareReferences) {
-        super(key, userSource, guts, name, subkind, null, declareReferences, null);
+            Collection<String> declareReferences,
+            DiagList syntheticDiags) {
+        super(key, userSource, guts, name, subkind, null, declareReferences,
+                null, syntheticDiags);
         this.typeName = typeName;
     }
 
--- /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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<SnippetWrapper> 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<Snippet> 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);
+    }
+}