8191842: JShell: Inferred type information is lost when assigning types to a \"var\"
authorjlahoda
Fri, 19 Jan 2018 17:11:52 +0100
changeset 48610 a587f95313f1
parent 48609 be4d948d1299
child 48637 7bba05746c44
8191842: JShell: Inferred type information is lost when assigning types to a \"var\" Summary: For vars, upgrading all anonymous classes to member classes; stripping intersection types from fields before writing. Reviewed-by: rfield
src/jdk.jshell/share/classes/jdk/jshell/Eval.java
src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java
src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java
src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java
src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java
src/jdk.jshell/share/classes/jdk/jshell/Util.java
src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java
src/jdk.jshell/share/classes/jdk/jshell/Wrap.java
test/langtools/jdk/jshell/InaccessibleExpressionTest.java
test/langtools/jdk/jshell/ToolSimpleTest.java
test/langtools/jdk/jshell/TypeNameTest.java
test/langtools/jdk/jshell/VariablesTest.java
--- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -36,6 +36,7 @@
 import com.sun.source.tree.ArrayTypeTree;
 import com.sun.source.tree.AssignmentTree;
 import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionStatementTree;
 import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.MethodTree;
@@ -43,6 +44,7 @@
 import com.sun.source.tree.NewClassTree;
 import com.sun.source.tree.Tree;
 import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreeScanner;
 import com.sun.tools.javac.tree.JCTree;
 import com.sun.tools.javac.tree.Pretty;
 import java.io.IOException;
@@ -52,6 +54,8 @@
 import java.util.LinkedHashSet;
 import java.util.Set;
 import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo;
+import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription;
+import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc;
 import jdk.jshell.Key.ErroneousKey;
 import jdk.jshell.Key.MethodKey;
 import jdk.jshell.Key.TypeDeclKey;
@@ -60,6 +64,7 @@
 import jdk.jshell.TaskFactory.AnalyzeTask;
 import jdk.jshell.TaskFactory.BaseTask;
 import jdk.jshell.TaskFactory.ParseTask;
+import jdk.jshell.Util.Pair;
 import jdk.jshell.Wrap.CompoundWrap;
 import jdk.jshell.Wrap.Range;
 import jdk.jshell.Snippet.Status;
@@ -74,6 +79,7 @@
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
 import static java.util.Collections.singletonList;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
 import static jdk.jshell.Util.DOIT_METHOD_NAME;
 import static jdk.jshell.Util.PREFIX_PATTERN;
@@ -98,6 +104,11 @@
 
     private int varNumber = 0;
 
+    /* The number of anonymous innerclasses seen so far. Used to generate unique
+     * names of these classes.
+     */
+    private int anonCount = 0;
+
     private final JShell state;
 
     // The set of names of methods on Object
@@ -203,7 +214,7 @@
                 case VARIABLE:
                     return processVariables(userSource, units, compileSourceInt, pt);
                 case EXPRESSION_STATEMENT:
-                    return processExpression(userSource, compileSourceInt);
+                    return processExpression(userSource, unitTree, compileSourceInt, pt);
                 case CLASS:
                     return processClass(userSource, unitTree, compileSourceInt, SubKind.CLASS_SUBKIND, pt);
                 case ENUM:
@@ -285,15 +296,19 @@
             String name = vt.getName().toString();
             String typeName;
             String fullTypeName;
+            String displayType;
+            boolean hasEnhancedType = false;
             TreeDependencyScanner tds = new TreeDependencyScanner();
             Wrap typeWrap;
             Wrap anonDeclareWrap = null;
             Wrap winit = null;
+            boolean enhancedDesugaring = false;
+            Set<String> anonymousClasses = Collections.emptySet();
             StringBuilder sbBrackets = new StringBuilder();
             Tree baseType = vt.getType();
             if (baseType != null) {
                 tds.scan(baseType); // Not dependent on initializer
-                fullTypeName = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
+                fullTypeName = displayType = typeName = EvalPretty.prettyExpr((JCTree) vt.getType(), false);
                 while (baseType instanceof ArrayTypeTree) {
                     //TODO handle annotations too
                     baseType = ((ArrayTypeTree) baseType).getType();
@@ -311,83 +326,27 @@
                     Range rinit = dis.treeToRange(init);
                     String initCode = rinit.part(compileSource);
                     ExpressionInfo ei =
-                            ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state);
-                    typeName = ei == null ? "java.lang.Object" : ei.typeName;
-                    fullTypeName = ei == null ? "java.lang.Object" : ei.fullTypeName;
-                    if (ei != null && init.getKind() == Tree.Kind.NEW_CLASS &&
-                        ((NewClassTree) init).getClassBody() != null) {
-                        NewClassTree nct = (NewClassTree) init;
-                        StringBuilder constructor = new StringBuilder();
-                        constructor.append(fullTypeName).append("(");
-                        String sep = "";
-                        if (ei.enclosingInstanceType != null) {
-                            constructor.append(ei.enclosingInstanceType);
-                            constructor.append(" encl");
-                            sep = ", ";
-                        }
-                        int idx = 0;
-                        for (String type : ei.parameterTypes) {
-                            constructor.append(sep);
-                            constructor.append(type);
-                            constructor.append(" ");
-                            constructor.append("arg" + idx++);
-                            sep = ", ";
-                        }
-                        if (ei.enclosingInstanceType != null) {
-                            constructor.append(") { encl.super (");
-                        } else {
-                            constructor.append(") { super (");
-                        }
-                        sep = "";
-                        for (int i = 0; i < idx; i++) {
-                            constructor.append(sep);
-                            constructor.append("arg" + i++);
-                            sep = ", ";
-                        }
-                        constructor.append("); }");
-                        List<? extends Tree> members = nct.getClassBody().getMembers();
-                        Range bodyRange = dis.treeListToRange(members);
-                        Wrap bodyWrap;
+                            ExpressionToTypeInfo.localVariableTypeForInitializer(initCode, state, false);
+                    if (ei != null) {
+                        typeName = ei.declareTypeName;
+                        fullTypeName = ei.fullTypeName;
+                        displayType = ei.displayTypeName;
+
+                        hasEnhancedType = !typeName.equals(fullTypeName);
 
-                        if (bodyRange != null) {
-                            bodyWrap = Wrap.rangeWrap(compileSource, bodyRange);
-                        } else {
-                            bodyWrap = Wrap.simpleWrap(" ");
-                        }
-
-                        Range argRange = dis.treeListToRange(nct.getArguments());
-                        Wrap argWrap;
-
-                        if (argRange != null) {
-                            argWrap = Wrap.rangeWrap(compileSource, argRange);
-                        } else {
-                            argWrap = Wrap.simpleWrap(" ");
-                        }
+                        enhancedDesugaring = !ei.isPrimitiveType;
 
-                        if (ei.enclosingInstanceType != null) {
-                            Range enclosingRanges =
-                                    dis.treeToRange(nct.getEnclosingExpression());
-                            Wrap enclosingWrap = Wrap.rangeWrap(compileSource, enclosingRanges);
-                            argWrap = argRange != null ? new CompoundWrap(enclosingWrap,
-                                                                          Wrap.simpleWrap(","),
-                                                                          argWrap)
-                                                       : enclosingWrap;
-                        }
-                        Wrap hwrap = Wrap.simpleWrap("public static class " + fullTypeName +
-                                                     (ei.isClass ? " extends " : " implements ") +
-                                                     typeName + " { " + constructor);
-                        anonDeclareWrap = new CompoundWrap(hwrap, bodyWrap, Wrap.simpleWrap("}"));
-                        winit = new CompoundWrap("new " + fullTypeName + "(", argWrap, ")");
-
-                        String superType = typeName;
-
-                        typeName = fullTypeName;
-                        fullTypeName = ei.isClass ? "<anonymous class extending " + superType + ">"
-                                                  : "<anonymous class implementing " + superType + ">";
+                        Pair<Wrap, Wrap> anonymous2Member =
+                                anonymous2Member(ei, compileSource, rinit, dis, init);
+                        anonDeclareWrap = anonymous2Member.first;
+                        winit = anonymous2Member.second;
+                        anonymousClasses = ei.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet());
+                    } else {
+                        displayType = fullTypeName = typeName = "java.lang.Object";
                     }
                     tds.scan(init);
                 } else {
-                    fullTypeName = typeName = "java.lang.Object";
+                    displayType = fullTypeName = typeName = "java.lang.Object";
                 }
                 typeWrap = Wrap.identityWrap(typeName);
             }
@@ -411,17 +370,193 @@
             int nameEnd = nameStart + name.length();
             Range rname = new Range(nameStart, nameEnd);
             Wrap guts = Wrap.varWrap(compileSource, typeWrap, sbBrackets.toString(), rname,
-                                     winit, anonDeclareWrap);
-                        DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
+                                     winit, enhancedDesugaring, anonDeclareWrap);
+            DiagList modDiag = modifierDiagnostics(vt.getModifiers(), dis, true);
             Snippet snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
-                    name, subkind, fullTypeName,
+                    name, subkind, displayType, hasEnhancedType ? fullTypeName : null, anonymousClasses,
                     tds.declareReferences(), modDiag);
             snippets.add(snip);
         }
         return snippets;
     }
 
-    private List<Snippet> processExpression(String userSource, String compileSource) {
+    /**Convert anonymous classes in "init" to member classes, based
+     * on the additional information from ExpressionInfo.anonymousClasses.
+     *
+     * This means:
+     * -if the code in the anonymous class captures any variables from the
+     *  enclosing context, create fields for them
+     * -creating an explicit constructor that:
+     * --if the new class expression has a base/enclosing expression, make it an
+     *   explicit constructor parameter "encl" and use "encl.super" when invoking
+     *   the supertype constructor
+     * --if the (used) supertype constructor has any parameters, declare them
+     *   as explicit parameters of the constructor, and pass them to the super
+     *   constructor
+     * --if the code in the anonymous class captures any variables from the
+     *   enclosing context, make them an explicit paramters of the constructor
+     *   and assign to respective fields.
+     * --if there are any explicit fields with initializers in the anonymous class,
+     *   move the initializers at the end of the constructor (after the captured fields
+     *   are assigned, so that the initializers of these fields can use them).
+     * -from the captured variables fields, constructor, and existing members
+     *  (with cleared field initializers), create an explicit class that extends or
+     *  implements the supertype of the anonymous class.
+     *
+     * This method returns two wraps: the first contains the class declarations for the
+     * converted classes, the first one should be used instead of "init" in the variable
+     * declaration.
+     */
+    private Pair<Wrap, Wrap> anonymous2Member(ExpressionInfo ei,
+                                              String compileSource,
+                                              Range rinit,
+                                              TreeDissector dis,
+                                              Tree init) {
+        List<Wrap> anonymousDeclarations = new ArrayList<>();
+        List<Wrap> partitionedInit = new ArrayList<>();
+        int lastPos = rinit.begin;
+        com.sun.tools.javac.util.List<NewClassTree> toConvert =
+                ExpressionToTypeInfo.listAnonymousClassesToConvert(init);
+        com.sun.tools.javac.util.List<AnonymousDescription> descriptions =
+                ei.anonymousClasses;
+        while (toConvert.nonEmpty() && descriptions.nonEmpty()) {
+            NewClassTree node = toConvert.head;
+            AnonymousDescription ad = descriptions.head;
+
+            toConvert = toConvert.tail;
+            descriptions = descriptions.tail;
+
+            List<Object> classBodyParts = new ArrayList<>();
+            //declarations of the captured variables:
+            for (VariableDesc vd : ad.capturedVariables) {
+                classBodyParts.add(vd.type + " " + vd.name + ";\n");
+            }
+
+            List<Object> constructorParts = new ArrayList<>();
+            constructorParts.add(ad.declareTypeName + "(");
+            String sep = "";
+            //add the parameter for the base/enclosing expression, if any:
+            if (ad.enclosingInstanceType != null) {
+                constructorParts.add(ad.enclosingInstanceType + " encl");
+                sep = ", ";
+            }
+            int idx = 0;
+            //add parameters of the super constructor, if any:
+            for (String type : ad.parameterTypes) {
+                constructorParts.add(sep);
+                constructorParts.add(type + " " + "arg" + idx++);
+                sep = ", ";
+            }
+            //add parameters for the captured variables:
+            for (VariableDesc vd : ad.capturedVariables) {
+                constructorParts.add(sep);
+                constructorParts.add(vd.type + " " + "cap$" + vd.name);
+                sep = ", ";
+            }
+            //construct super constructor call:
+            if (ad.enclosingInstanceType != null) {
+                //if there's an enclosing instance, call super on it:
+                constructorParts.add(") { encl.super (");
+            } else {
+                constructorParts.add(") { super (");
+            }
+            sep = "";
+            for (int i = 0; i < idx; i++) {
+                constructorParts.add(sep);
+                constructorParts.add("arg" + i);
+                sep = ", ";
+            }
+            constructorParts.add(");");
+            //initialize the captured variables:
+            for (VariableDesc vd : ad.capturedVariables) {
+                constructorParts.add("this." + vd.name + " = " + "cap$" + vd.name + ";\n");
+            }
+            List<? extends Tree> members =
+                    node.getClassBody().getMembers();
+            for (Tree member : members) {
+                if (member.getKind() == Tree.Kind.VARIABLE) {
+                    VariableTree vt = (VariableTree) member;
+
+                    if (vt.getInitializer() != null) {
+                        //for variables with initializer, explicitly move the initializer
+                        //to the constructor after the captured variables as assigned
+                        //(the initializers would otherwise run too early):
+                        Range wholeVar = dis.treeToRange(vt);
+                        int name = ((JCTree) vt).pos;
+                        classBodyParts.add(new CompoundWrap(Wrap.rangeWrap(compileSource,
+                                                                      new Range(wholeVar.begin, name)),
+                                                       vt.getName().toString(),
+                                                       ";\n"));
+                        constructorParts.add(Wrap.rangeWrap(compileSource,
+                                                            new Range(name, wholeVar.end)));
+                        continue;
+                    }
+                }
+                classBodyParts.add(Wrap.rangeWrap(compileSource,
+                                             dis.treeToRange(member)));
+            }
+
+            constructorParts.add("}");
+
+            //construct the member class:
+            classBodyParts.add(new CompoundWrap(constructorParts.toArray()));
+
+            Wrap classBodyWrap = new CompoundWrap(classBodyParts.toArray());
+
+            anonymousDeclarations.add(new CompoundWrap("public static class ", ad.declareTypeName,
+                                         (ad.isClass ? " extends " : " implements "),
+                                         ad.superTypeName, " { ", classBodyWrap, "}"));
+
+            //change the new class expression to use the newly created member type:
+            Range argRange = dis.treeListToRange(node.getArguments());
+            Wrap argWrap;
+
+            if (argRange != null) {
+                argWrap = Wrap.rangeWrap(compileSource, argRange);
+            } else {
+                argWrap = Wrap.simpleWrap(" ");
+            }
+
+            if (ad.enclosingInstanceType != null) {
+                //if there's an enclosing expression, set it as the first parameter:
+                Range enclosingRanges =
+                        dis.treeToRange(node.getEnclosingExpression());
+                Wrap enclosingWrap = Wrap.rangeWrap(compileSource, enclosingRanges);
+                argWrap = argRange != null ? new CompoundWrap(enclosingWrap,
+                                                              Wrap.simpleWrap(","),
+                                                              argWrap)
+                                           : enclosingWrap;
+            }
+
+            Range current = dis.treeToRange(node);
+            String capturedArgs;
+            if (!ad.capturedVariables.isEmpty()) {
+                capturedArgs = (ad.parameterTypes.isEmpty() ? "" : ", ") +
+                               ad.capturedVariables.stream()
+                                                   .map(vd -> vd.name)
+                                                   .collect(Collectors.joining(","));
+            } else {
+                capturedArgs = "";
+            }
+            if (lastPos < current.begin)
+                partitionedInit.add(Wrap.rangeWrap(compileSource,
+                                                   new Range(lastPos, current.begin)));
+            partitionedInit.add(new CompoundWrap("new " + ad.declareTypeName + "(",
+                                                 argWrap,
+                                                 capturedArgs,
+                                                 ")"));
+            lastPos = current.end;
+        }
+
+        if (lastPos < rinit.end)
+            partitionedInit.add(Wrap.rangeWrap(compileSource, new Range(lastPos, rinit.end)));
+
+        return new Pair<>(new CompoundWrap(anonymousDeclarations.toArray()),
+                          new CompoundWrap(partitionedInit.toArray()));
+    }
+
+    private List<Snippet> processExpression(String userSource, Tree tree, String compileSource, ParseTask pt) {
+        ExpressionStatementTree expr = (ExpressionStatementTree) tree;
         String name = null;
         ExpressionInfo ei = ExpressionToTypeInfo.expressionInfo(compileSource, state);
         ExpressionTree assignVar;
@@ -453,10 +588,31 @@
                         name = "$" + ++varNumber;
                     }
                 }
-                guts = Wrap.tempVarWrap(compileSource, ei.accessibleTypeName, name);
+                TreeDissector dis = TreeDissector.createByFirstClass(pt);
+                ExpressionInfo varEI =
+                        ExpressionToTypeInfo.localVariableTypeForInitializer(compileSource, state, true);
+                String declareTypeName;
+                String fullTypeName;
+                String displayTypeName;
+                Set<String> anonymousClasses;
+                if (varEI != null) {
+                    declareTypeName = varEI.declareTypeName;
+                    fullTypeName = varEI.fullTypeName;
+                    displayTypeName = varEI.displayTypeName;
+
+                    Pair<Wrap, Wrap> anonymous2Member =
+                            anonymous2Member(varEI, compileSource, new Range(0, compileSource.length()), dis, expr.getExpression());
+                    guts = Wrap.tempVarWrap(anonymous2Member.second.wrapped(), declareTypeName, name, anonymous2Member.first);
+                    anonymousClasses = varEI.anonymousClasses.stream().map(ad -> ad.declareTypeName).collect(Collectors.toSet());
+                } else {
+                    declareTypeName = ei.accessibleTypeName;
+                    displayTypeName = fullTypeName = typeName;
+                    guts = Wrap.tempVarWrap(compileSource, declareTypeName, name, null);
+                    anonymousClasses = Collections.emptySet();
+                }
                 Collection<String> declareReferences = null; //TODO
                 snip = new VarSnippet(state.keyMap.keyForVariable(name), userSource, guts,
-                        name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, typeName, declareReferences, null);
+                        name, SubKind.TEMP_VAR_EXPRESSION_SUBKIND, displayTypeName, fullTypeName, anonymousClasses, declareReferences, null);
             } else {
                 guts = Wrap.methodReturnWrap(compileSource);
                 snip = new ExpressionSnippet(state.keyMap.keyForExpression(name, typeName), userSource, guts,
@@ -1059,4 +1215,7 @@
                 : new DiagList(new ModifierDiagnostic(list, fatal));
     }
 
+    String computeDeclareName(TypeSymbol ts) {
+        return Util.JSHELL_ANONYMOUS + "$" + Long.toUnsignedString(anonCount++);
+    }
 }
--- a/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/ExpressionToTypeInfo.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -25,12 +25,21 @@
 
 package jdk.jshell;
 
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.VariableElement;
 import com.sun.source.tree.ReturnTree;
 import com.sun.source.tree.ClassTree;
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.ConditionalExpressionTree;
-import com.sun.source.tree.ExpressionStatementTree;
 import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.MethodInvocationTree;
 import com.sun.source.tree.MethodTree;
 import com.sun.source.tree.NewClassTree;
@@ -39,13 +48,21 @@
 import com.sun.source.tree.VariableTree;
 import com.sun.source.util.TreePath;
 import com.sun.source.util.TreePathScanner;
+import com.sun.source.util.TreeScanner;
 import com.sun.tools.javac.code.Flags;
 import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Symtab;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.TreeInfo;
 import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription;
+import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo.AnonymousDescription.VariableDesc;
 import jdk.jshell.TaskFactory.AnalyzeTask;
+import jdk.jshell.TypePrinter.AnonymousTypeKind;
 
 /**
  * Compute information about an expression string, particularly its type name.
@@ -57,26 +74,75 @@
     final AnalyzeTask at;
     final CompilationUnitTree cu;
     final JShell state;
+    final boolean computeEnhancedInfo;
+    final boolean enhancedTypesAccessible;
     final Symtab syms;
     final Types types;
+    final Map<TypeSymbol, String> anon2Name = new HashMap<>();
 
-    private ExpressionToTypeInfo(AnalyzeTask at, CompilationUnitTree cu, JShell state) {
+    private ExpressionToTypeInfo(AnalyzeTask at, CompilationUnitTree cu, JShell state,
+                                 boolean computeEnhancedInfo, boolean enhancedTypesAccessible) {
         this.at = at;
         this.cu = cu;
         this.state = state;
+        this.computeEnhancedInfo = computeEnhancedInfo;
+        this.enhancedTypesAccessible = enhancedTypesAccessible;
         this.syms = Symtab.instance(at.context);
         this.types = Types.instance(at.context);
     }
 
     public static class ExpressionInfo {
         ExpressionTree tree;
+        boolean isPrimitiveType;
         String typeName;
         String accessibleTypeName;
+        /* In result of localVariableTypeForInitializer, the type that should be used
+         * as a declaration type of the field. This does not include intersection types,
+         * but does contain references to anonymous types converted to member types.
+         */
+        String declareTypeName;
+        /* In result of localVariableTypeForInitializer, the apparent/infered type of
+         * the variable. This includes intersection types, and references to anonymous
+         * types converted to member types.
+         */
         String fullTypeName;
-        List<String> parameterTypes;
-        String enclosingInstanceType;
-        boolean isClass;
+        /* In result of localVariableTypeForInitializer, the human readable type of
+         * the variable. This includes intersection types, and human readable descriptions
+         * of anonymous types.
+         */
+        String displayTypeName;
         boolean isNonVoid;
+        /* In result of localVariableTypeForInitializer, description of important anonymous
+         * classes.
+         */
+        List<AnonymousDescription> anonymousClasses = List.nil();
+
+        /* A description of an anonymous class. */
+        static class AnonymousDescription {
+            /* Parameter types of the invoked super constructor.*/
+            List<String> parameterTypes;
+            /* Type of the base/enclosing expression, if any.*/
+            String enclosingInstanceType;
+            /* The denotable name of the supertype.*/
+            String superTypeName;
+            /* The human-readable name of this class.*/
+            String declareTypeName;
+            /* If the supertype of this anonymous is a class. */
+            boolean isClass;
+            /* Variables captured by this anonymous class*/
+            List<VariableDesc> capturedVariables;
+
+            static class VariableDesc {
+                String type;
+                String name;
+
+                public VariableDesc(String type, String name) {
+                    this.type = type;
+                    this.name = name;
+                }
+
+            }
+        }
     }
 
     // return mechanism and other general structure from TreePath.getPath()
@@ -173,7 +239,7 @@
                 if (at.hasErrors() || cu == null) {
                     return null;
                 }
-                return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+                return new ExpressionToTypeInfo(at, cu, state, false, false).typeOfExpression();
             });
         } catch (Exception ex) {
             return null;
@@ -187,7 +253,7 @@
      * @param state a JShell instance
      * @return type information
      */
-    public static ExpressionInfo localVariableTypeForInitializer(String code, JShell state) {
+    public static ExpressionInfo localVariableTypeForInitializer(String code, JShell state, boolean onlyAccessible) {
         if (code == null || code.isEmpty()) {
             return null;
         }
@@ -198,13 +264,37 @@
                 if (at.hasErrors() || cu == null) {
                     return null;
                 }
-                return new ExpressionToTypeInfo(at, cu, state).typeOfExpression();
+                return new ExpressionToTypeInfo(at, cu, state, true, onlyAccessible)
+                        .typeOfExpression();
             });
         } catch (Exception ex) {
             return null;
         }
     }
 
+    /**List (in a stable order) all NewClassTree instances under {@code from} that should be
+     * converted to member classes
+     *
+     * @param from tree to inspect
+     * @return NewClassTree instances that should be converted to member classes
+     */
+    public static List<NewClassTree> listAnonymousClassesToConvert(Tree from) {
+        ListBuffer<NewClassTree> classes = new ListBuffer<>();
+
+        new TreeScanner<Void, Void>() {
+            @Override
+            public Void visitNewClass(NewClassTree node, Void p) {
+                if (node.getClassBody() != null) {
+                    classes.append(node);
+                    return null;
+                }
+                return super.visitNewClass(node, p);
+            }
+        }.scan(from, null);
+
+        return classes.toList();
+    }
+
     private ExpressionInfo typeOfExpression() {
         return treeToInfo(findExpressionPath());
     }
@@ -256,23 +346,31 @@
      * @return the type, if it is accessible, otherwise a superclass or
      * interface which is
      */
-    private Type findAccessibleSupertype(Type type) {
+    private List<Type> findAccessibleSupertypes(Type type) {
+        List<Type> accessible = List.nil();
+        Type accessibleSuper = syms.objectType;
         // Iterate up the superclasses, see if any are accessible
         for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
             if (isAccessible(sup)) {
-                return sup;
+                accessible = accessible.prepend(sup);
+                accessibleSuper = sup;
+                break;
             }
         }
-        // Failing superclasses, look through superclasses for accessible interfaces
-        for (Type sup = type; !types.isSameType(sup, syms.objectType); sup = supertype(sup)) {
+        // then look through superclasses for accessible interfaces
+        for (Type sup = type; !types.isSameType(sup, accessibleSuper); sup = supertype(sup)) {
             for (Type itf : types.interfaces(sup)) {
                 if (isAccessible(itf)) {
-                    return itf;
+                    accessible = accessible.prepend(itf);
                 }
             }
         }
-        // Punt, return Object which is the supertype of everything
-        return syms.objectType;
+        if (accessible.isEmpty()) {
+            // Punt, use Object which is the supertype of everything
+            accessible = accessible.prepend(syms.objectType);
+        }
+
+        return accessible.reverse();
     }
 
     private ExpressionInfo treeToInfo(TreePath tp) {
@@ -298,48 +396,125 @@
                             break;
                         default: {
                             ei.isNonVoid = true;
-                            ei.typeName = varTypeName(type, false);
-                            ei.accessibleTypeName = varTypeName(findAccessibleSupertype(type), false);
-                            ei.fullTypeName = varTypeName(type, true);
+                            ei.isPrimitiveType = type.isPrimitive();
+                            ei.typeName = varTypeName(type, false, AnonymousTypeKind.SUPER);
+                            List<Type> accessibleTypes = findAccessibleSupertypes(type);
+                            ei.accessibleTypeName =
+                                    varTypeName(accessibleTypes.head, false, AnonymousTypeKind.SUPER);
+                            if (computeEnhancedInfo) {
+                                Type accessibleType = accessibleTypes.size() == 1 ? accessibleTypes.head
+                                            : types.makeIntersectionType(accessibleTypes);
+                                ei.declareTypeName =
+                                        varTypeName(accessibleType, false, AnonymousTypeKind.DECLARE);
+                                ei.fullTypeName =
+                                        varTypeName(enhancedTypesAccessible ? accessibleType : type,
+                                                    true, AnonymousTypeKind.DECLARE);
+                                ei.displayTypeName =
+                                        varTypeName(type, true, AnonymousTypeKind.DISPLAY);
+                            }
                             break;
                         }
                     }
                 }
-                if (tree.getKind() == Tree.Kind.VARIABLE) {
+                if (tree.getKind() == Tree.Kind.VARIABLE && computeEnhancedInfo) {
                     Tree init = ((VariableTree) tree).getInitializer();
-                    if (init.getKind() == Tree.Kind.NEW_CLASS &&
-                        ((NewClassTree) init).getClassBody() != null) {
-                        NewClassTree nct = (NewClassTree) init;
-                        ClassTree clazz = nct.getClassBody();
-                        MethodTree constructor = (MethodTree) clazz.getMembers().get(0);
-                        ExpressionStatementTree superCallStatement =
-                                (ExpressionStatementTree) constructor.getBody().getStatements().get(0);
+                    for (NewClassTree node : listAnonymousClassesToConvert(init)) {
+                        Set<VariableElement> captured = capturedVariables(at,
+                                                                          tp.getCompilationUnit(),
+                                                                          node);
+                        JCClassDecl clazz = (JCClassDecl) node.getClassBody();
                         MethodInvocationTree superCall =
-                                (MethodInvocationTree) superCallStatement.getExpression();
-                        TreePath superCallPath =
-                                at.trees().getPath(tp.getCompilationUnit(), superCall.getMethodSelect());
+                                clazz.getMembers()
+                                     .stream()
+                                     .map(TreeInfo::firstConstructorCall)
+                                     .findAny()
+                                     .get();
+                        TreePath superCallPath
+                                = at.trees().
+                                        getPath(tp.getCompilationUnit(), superCall.
+                                                getMethodSelect());
                         Type constrType = pathToType(superCallPath);
-                        ei.parameterTypes = constrType.getParameterTypes()
-                                                      .stream()
-                                                      .map(t -> varTypeName(t, false))
-                                                      .collect(List.collector());
-                        if (nct.getEnclosingExpression() != null) {
-                            TreePath enclPath = new TreePath(tp, nct.getEnclosingExpression());
-                            ei.enclosingInstanceType = varTypeName(pathToType(enclPath), false);
+                        AnonymousDescription desc = new AnonymousDescription();
+                        desc.parameterTypes = constrType.getParameterTypes().
+                                stream().
+                                map(t -> varTypeName(t, false, AnonymousTypeKind.DECLARE)).
+                                collect(List.collector());
+                        if (node.getEnclosingExpression() != null) {
+                            TreePath enclPath = new TreePath(tp,
+                                                             node.getEnclosingExpression());
+                            desc.enclosingInstanceType = varTypeName(pathToType(enclPath),
+                                                                     false,
+                                                                     AnonymousTypeKind.DECLARE);
                         }
-                        ei.isClass = at.task.getTypes().directSupertypes(type).size() == 1;
+                        TreePath currentPath = at.trees()
+                                                 .getPath(tp.getCompilationUnit(),
+                                                          node);
+                        Type nodeType = pathToType(currentPath, node);
+                        desc.superTypeName = varTypeName(nodeType,
+                                                         false,
+                                                         AnonymousTypeKind.SUPER);
+                        desc.declareTypeName = varTypeName(nodeType,
+                                                           true, AnonymousTypeKind.DECLARE);
+                        desc.capturedVariables =
+                                captured.stream()
+                                        .map(ve -> new VariableDesc(varTypeName((Type) ve.asType(),
+                                                                                false,
+                                                                                AnonymousTypeKind.DECLARE),
+                                                                    ve.getSimpleName().toString()))
+                                        .collect(List.collector());
+
+                        desc.isClass = at.task.getTypes().directSupertypes(nodeType).size() == 1;
+                        ei.anonymousClasses = ei.anonymousClasses.prepend(desc);
                     }
+                    ei.anonymousClasses = ei.anonymousClasses.reverse();
                 }
                 return ei;
             }
         }
         return null;
     }
+    //where:
+        private static Set<VariableElement> capturedVariables(AnalyzeTask at,
+                                                              CompilationUnitTree topLevel,
+                                                              Tree tree) {
+            Set<VariableElement> capturedVars = new HashSet<>();
+            new TreeScanner<Void, Void>() {
+                Set<VariableElement> declaredLocalVars = new HashSet<>();
+                @Override
+                public Void visitVariable(VariableTree node, Void p) {
+                    TreePath currentPath = at.trees()
+                                             .getPath(topLevel, node);
+                    declaredLocalVars.add((VariableElement) at.trees().getElement(currentPath));
+                    return super.visitVariable(node, p);
+                }
 
-    private String varTypeName(Type type, boolean printIntersectionTypes) {
+                @Override
+                public Void visitIdentifier(IdentifierTree node, Void p) {
+                    TreePath currentPath = at.trees()
+                                             .getPath(topLevel, node);
+                    Element el = at.trees().getElement(currentPath);
+                    if (el != null &&
+                        LOCAL_VARIABLES.contains(el.getKind()) &&
+                        !declaredLocalVars.contains(el)) {
+                        capturedVars.add((VariableElement) el);
+                    }
+                    return super.visitIdentifier(node, p);
+                }
+            }.scan(tree, null);
+
+            return capturedVars;
+        }
+        private static final Set<ElementKind> LOCAL_VARIABLES =
+                EnumSet.of(ElementKind.EXCEPTION_PARAMETER, ElementKind.LOCAL_VARIABLE,
+                           ElementKind.PARAMETER, ElementKind.RESOURCE_VARIABLE);
+
+    private String varTypeName(Type type, boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
         try {
+            Function<TypeSymbol, String> anonymousClass2DeclareName =
+                    cs -> anon2Name.computeIfAbsent(cs, state.eval::computeDeclareName);
             TypePrinter tp = new TypePrinter(at.messages(),
-                    state.maps::fullClassNameAndPackageToClass, printIntersectionTypes);
+                    state.maps::fullClassNameAndPackageToClass, anonymousClass2DeclareName,
+                    printIntersectionTypes, anonymousTypesKind);
             List<Type> captures = types.captures(type);
             String res = tp.toString(types.upward(type, captures));
 
--- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -61,24 +61,33 @@
 import java.lang.Runtime.Version;
 import java.nio.CharBuffer;
 import java.util.function.BiFunction;
+import com.sun.source.tree.ClassTree;
 import com.sun.source.tree.Tree.Kind;
 import com.sun.source.util.TaskEvent;
 import com.sun.source.util.TaskListener;
 import com.sun.tools.javac.api.JavacTaskPool;
 import com.sun.tools.javac.code.ClassFinder;
 import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.comp.Attr;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Resolve;
 import com.sun.tools.javac.parser.Parser;
 import com.sun.tools.javac.parser.ParserFactory;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
 import com.sun.tools.javac.tree.JCTree.JCExpression;
 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
 import com.sun.tools.javac.tree.JCTree.Tag;
+import com.sun.tools.javac.util.Context.Factory;
 import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
 import com.sun.tools.javac.util.Names;
@@ -197,6 +206,7 @@
                                          compilationUnits, task -> {
                  JavacTaskImpl jti = (JavacTaskImpl) task;
                  Context context = jti.getContext();
+                 DisableAccessibilityResolve.preRegister(context);
                  jti.addTaskListener(new TaskListenerImpl(context, state));
                  try {
                      return worker.withTask(creator.apply(jti, diagnostics));
@@ -578,10 +588,25 @@
         }
     }
 
+    /**The variable types inferred for "var"s may be non-denotable.
+     * jshell desugars these variables into fields, and fields must have
+     * a denotable type. So these fields are declared with some simpler denotable
+     * type, and the listener here enhances the types of the fields to be the full
+     * inferred types. This is mainly when the inferred type contains:
+     * -intersection types (e.g. <Z extends Runnable&CharSequence> Z get() {...} var z = get();)
+     * -types that are inaccessible at the given place
+     *
+     * This type enhancement does not need to do anything about anonymous classes, as these
+     * are desugared into member classes.
+     */
     private static final class TaskListenerImpl implements TaskListener {
 
         private final Context context;
         private final JShell state;
+        /* Keep the original (declaration) types of the fields that were enhanced.
+         * The declaration types need to be put back before writing the fields
+         * into classfiles.*/
+        private final Map<VarSymbol, Type> var2OriginalType = new HashMap<>();
 
         public TaskListenerImpl(Context context, JShell state) {
             this.context = context;
@@ -589,43 +614,85 @@
         }
 
         @Override
+        public void started(TaskEvent e) {
+            if (e.getKind() != TaskEvent.Kind.GENERATE)
+                return ;
+            //clear enhanced types in fields we are about to write to the classfiles:
+            for (Tree clazz : e.getCompilationUnit().getTypeDecls()) {
+                ClassTree ct = (ClassTree) clazz;
+
+                for (Tree member : ct.getMembers()) {
+                    if (member.getKind() != Tree.Kind.VARIABLE)
+                        continue;
+                    VarSymbol vsym = ((JCVariableDecl) member).sym;
+                    Type original = var2OriginalType.remove(vsym);
+                    if (original != null) {
+                        vsym.type = original;
+                    }
+                }
+            }
+        }
+
+        private boolean variablesSet = false;
+
+        @Override
         public void finished(TaskEvent e) {
-            if (e.getKind() != TaskEvent.Kind.ENTER)
+            if (e.getKind() != TaskEvent.Kind.ENTER || variablesSet)
                 return ;
             state.maps
                  .snippetList()
                  .stream()
                  .filter(s -> s.status() == Status.VALID)
                  .filter(s -> s.kind() == Snippet.Kind.VAR)
-                 .filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)
-                 .forEach(s -> setVariableType((JCCompilationUnit) e.getCompilationUnit(), (VarSnippet) s));
+                 .filter(s -> s.subKind() == Snippet.SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND ||
+                              s.subKind() == Snippet.SubKind.TEMP_VAR_EXPRESSION_SUBKIND)
+                 .forEach(s -> setVariableType((VarSnippet) s));
+            variablesSet = true;
         }
 
-        private void setVariableType(JCCompilationUnit root, VarSnippet s) {
+        /* If the snippet contain enhanced types, enhance the type of
+         * the variable from snippet s to be the enhanced type.
+         */
+        private void setVariableType(VarSnippet s) {
+            String typeName = s.fullTypeName;
+
+            if (typeName == null)
+                return ;
+
             Symtab syms = Symtab.instance(context);
             Names names = Names.instance(context);
             Log log  = Log.instance(context);
             ParserFactory parserFactory = ParserFactory.instance(context);
             Attr attr = Attr.instance(context);
+            Enter enter = Enter.instance(context);
+            DisableAccessibilityResolve rs = (DisableAccessibilityResolve) Resolve.instance(context);
 
+            //find the variable:
             ClassSymbol clazz = syms.getClass(syms.unnamedModule, names.fromString(s.classFullName()));
             if (clazz == null || !clazz.isCompleted())
                 return;
             VarSymbol field = (VarSymbol) clazz.members().findFirst(names.fromString(s.name()), sym -> sym.kind == Kinds.Kind.VAR);
-            if (field != null) {
+
+            if (field != null && !var2OriginalType.containsKey(field)) {
+                //if it was not enhanced yet:
+                //ignore any errors:
                 JavaFileObject prev = log.useSource(null);
                 DiscardDiagnosticHandler h = new DiscardDiagnosticHandler(log);
                 try {
-                    String typeName = s.typeName();
+                    //parse the type as a cast, i.e. "(<typeName>) x". This is to support
+                    //intersection types:
                     CharBuffer buf = CharBuffer.wrap(("(" + typeName +")x\u0000").toCharArray(), 0, typeName.length() + 3);
                     Parser parser = parserFactory.newParser(buf, false, false, false);
                     JCExpression expr = parser.parseExpression();
                     if (expr.hasTag(Tag.TYPECAST)) {
+                        //if parsed OK, attribute and set the type:
+                        var2OriginalType.put(field, field.type);
+
                         JCTypeCast tree = (JCTypeCast) expr;
-                        if (tree.clazz.hasTag(Tag.TYPEINTERSECTION)) {
+                        rs.runWithoutAccessChecks(() -> {
                             field.type = attr.attribType(tree.clazz,
-                                                         ((JCClassDecl) root.getTypeDecls().head).sym);
-                        }
+                                                         enter.getEnvs().iterator().next().enclClass.sym);
+                        });
                     }
                 } finally {
                     log.popDiagnosticHandler(h);
@@ -635,4 +702,48 @@
         }
     }
 
+    private static final class DisableAccessibilityResolve extends Resolve {
+
+        public static void preRegister(Context context) {
+            if (context.get(Marker.class) == null) {
+                context.put(resolveKey, ((Factory<Resolve>) c -> new DisableAccessibilityResolve(c)));
+                context.put(Marker.class, new Marker());
+            }
+        }
+
+        private boolean noAccessChecks;
+
+        public DisableAccessibilityResolve(Context context) {
+            super(context);
+        }
+
+        /**Run the given Runnable with all access checks disabled.
+         *
+         * @param r Runnnable to run
+         */
+        public void runWithoutAccessChecks(Runnable r) {
+            boolean prevNoAccessCheckes = noAccessChecks;
+            try {
+                noAccessChecks = true;
+                r.run();
+            } finally {
+                noAccessChecks = prevNoAccessCheckes;
+            }
+        }
+
+        @Override
+        public boolean isAccessible(Env<AttrContext> env, TypeSymbol c, boolean checkInner) {
+            if (noAccessChecks) return true;
+            return super.isAccessible(env, c, checkInner);
+        }
+
+        @Override
+        public boolean isAccessible(Env<AttrContext> env, Type site, Symbol sym, boolean checkInner) {
+            if (noAccessChecks) return true;
+            return super.isAccessible(env, site, sym, checkInner);
+        }
+
+        private static final class Marker {}
+    }
+
 }
--- a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -48,6 +48,7 @@
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 import javax.lang.model.type.TypeMirror;
+import jdk.jshell.TypePrinter.AnonymousTypeKind;
 import jdk.jshell.Util.Pair;
 
 /**
@@ -227,7 +228,7 @@
         Type typeImpl = (Type) type;
         try {
             TypePrinter tp = new TypePrinter(at.messages(),
-                    state.maps::fullClassNameAndPackageToClass, true);
+                    state.maps::fullClassNameAndPackageToClass, true, AnonymousTypeKind.DISPLAY);
             return tp.toString(typeImpl);
         } catch (Exception ex) {
             return null;
--- a/src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/TypePrinter.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -30,14 +30,17 @@
 import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
 import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Type.ClassType;
 import com.sun.tools.javac.code.Type.IntersectionClassType;
 import com.sun.tools.javac.util.JavacMessages;
 import java.util.Locale;
 import java.util.function.BinaryOperator;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
+
 /**
  * Print types in source form.
  */
@@ -47,14 +50,44 @@
 
     private final JavacMessages messages;
     private final BinaryOperator<String> fullClassNameAndPackageToClass;
-    private final boolean printEnhancedTypes;
+    private final Function<TypeSymbol, String> anonymousToName;
+    private final boolean printIntersectionTypes;
+    private final AnonymousTypeKind anonymousTypesKind;
 
+    /**Create a TypePrinter.
+     *
+     * @param messages javac's messages
+     * @param fullClassNameAndPackageToClass convertor to convert full class names to
+     *                                       simple class names.
+     * @param printIntersectionTypes whether intersection types should be printed
+     * @param anonymousTypesKind how the anonymous types should be printed
+     */
     TypePrinter(JavacMessages messages,
                 BinaryOperator<String> fullClassNameAndPackageToClass,
-                boolean printEnhancedTypes) {
+                boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
+        this(messages, fullClassNameAndPackageToClass, cs -> cs.flatName().toString(),
+             printIntersectionTypes, anonymousTypesKind);
+    }
+
+    /**Create a TypePrinter.
+     *
+     * @param messages javac's messages
+     * @param fullClassNameAndPackageToClass convertor to convert full class names to
+     *                                       simple class names.
+     * @param anonymousToName convertor from anonymous classes to name that should be printed
+     *                        if anonymousTypesKind == AnonymousTypeKind.DECLARE
+     * @param printIntersectionTypes whether intersection types should be printed
+     * @param anonymousTypesKind how the anonymous types should be printed
+     */
+    TypePrinter(JavacMessages messages,
+                BinaryOperator<String> fullClassNameAndPackageToClass,
+                Function<TypeSymbol, String> anonymousToName,
+                boolean printIntersectionTypes, AnonymousTypeKind anonymousTypesKind) {
         this.messages = messages;
         this.fullClassNameAndPackageToClass = fullClassNameAndPackageToClass;
-        this.printEnhancedTypes = printEnhancedTypes;
+        this.anonymousToName = anonymousToName;
+        this.printIntersectionTypes = printIntersectionTypes;
+        this.anonymousTypesKind = anonymousTypesKind;
     }
 
     String toString(Type t) {
@@ -96,9 +129,9 @@
      */
     @Override
     protected String className(ClassType t, boolean longform, Locale locale) {
-        Symbol sym = t.tsym;
+        TypeSymbol sym = t.tsym;
         if (sym.name.length() == 0 && (sym.flags() & COMPOUND) != 0) {
-            if (printEnhancedTypes) {
+            if (printIntersectionTypes) {
                 return ((IntersectionClassType) t).getExplicitComponents()
                                                   .stream()
                                                   .map(i -> visit(i, locale))
@@ -107,18 +140,26 @@
                 return OBJECT;
             }
         } else if (sym.name.length() == 0) {
-            if (printEnhancedTypes) {
-                return t.tsym.flatName().toString().substring(t.tsym.outermostClass().flatName().length());
+            if (anonymousTypesKind == AnonymousTypeKind.DECLARE) {
+                return anonymousToName.apply(sym);
             }
             // Anonymous
             String s;
+            boolean isClass;
             ClassType norm = (ClassType) t.tsym.type;
             if (norm == null) {
                 s = OBJECT;
+                isClass = true;
             } else if (norm.interfaces_field != null && norm.interfaces_field.nonEmpty()) {
                 s = visit(norm.interfaces_field.head, locale);
+                isClass = false;
             } else {
                 s = visit(norm.supertype_field, locale);
+                isClass = true;
+            }
+            if (anonymousTypesKind == AnonymousTypeKind.DISPLAY) {
+                s = isClass ? "<anonymous class extending " + s + ">"
+                            : "<anonymous class implementing " + s + ">";
             }
             return s;
         } else if (longform) {
@@ -152,4 +193,14 @@
                 : s.fullname.toString();
     }
 
+    /** Specifies how the anonymous classes should be handled. */
+    public enum AnonymousTypeKind {
+        /* The anonymous class is printed as the name of its supertype. */
+        SUPER,
+        /* The anonymous class is printed as converted by the anonymousToName
+         * convertor. */
+        DECLARE,
+        /* The anonymous class is printed in a human readable form. */
+        DISPLAY;
+    }
 }
--- a/src/jdk.jshell/share/classes/jdk/jshell/Util.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Util.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -56,6 +56,11 @@
     static final String DOIT_METHOD_NAME = "do_it$";
 
     /**
+     * The prefix for all anonymous classes upgraded to member classes.
+     */
+    static final String JSHELL_ANONYMOUS = "$JShell$anonymous$";
+
+    /**
      * A pattern matching the full or simple class name of a wrapper class.
      */
     static final Pattern PREFIX_PATTERN = Pattern.compile(
--- a/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/VarSnippet.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -26,6 +26,8 @@
 package jdk.jshell;
 
 import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
 import jdk.jshell.Key.VarKey;
 
 /**
@@ -41,15 +43,30 @@
  */
 public class VarSnippet extends DeclarationSnippet {
 
+    /**A human readable type of the variable. May include intersection types
+     * and human readable description of anonymous classes.
+     */
     final String typeName;
 
+    /**The full type inferred for "var" variables. May include intersection types
+     * and inaccessible types. {@literal null} if enhancing the type is not necessary.
+     */
+    final String fullTypeName;
+
+    /**The anonymous class declared in the initializer of the "var" variable.
+     * These are automatically statically imported when the field is imported.
+     */
+    final Set<String> anonymousClasses;
+
      VarSnippet(VarKey key, String userSource, Wrap guts,
-            String name, SubKind subkind, String typeName,
-            Collection<String> declareReferences,
+            String name, SubKind subkind, String typeName, String fullTypeName,
+            Set<String> anonymousClasses, Collection<String> declareReferences,
             DiagList syntheticDiags) {
         super(key, userSource, guts, name, subkind, null, declareReferences,
                 null, syntheticDiags);
         this.typeName = typeName;
+        this.fullTypeName = fullTypeName;
+        this.anonymousClasses = anonymousClasses;
     }
 
     /**
@@ -59,4 +76,13 @@
     public String typeName() {
         return typeName;
     }
+
+    @Override
+    String importLine(JShell state) {
+        return "import static " + classFullName() + "." + name() + ";\n" +
+               anonymousClasses.stream()
+                               .map(c -> "import static " + classFullName() + "." + c + ";\n")
+                               .collect(Collectors.joining());
+    }
+
 }
--- a/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Wrap.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -25,7 +25,9 @@
 
 package jdk.jshell;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import static java.util.stream.Collectors.joining;
 import static jdk.jshell.Util.DOIT_METHOD_NAME;
 
@@ -66,37 +68,76 @@
         return "\n" + indent(n);
     }
 
-    /**
+    /**Create a stub of a compilable representation of a variable snippet.
+     * The variable is always represented by a field. If the variable
+     * in the snippet has an initializer, the field is initialized by
+     * calling the DOIT_METHOD_NAME method.
      *
-     * @param in
-     * @param rname
-     * @param rinit Initializer or null
-     * @param rdecl Type name and name
-     * @return
+     * In some cases, the real inferred type of the variable may be non-denotable
+     * (e.g. intersection types). The declared type of the field must always
+     * be denotable (i.e. such that it can be written into the classfile), but
+     * if the real type is potentially non-denotable, the {@code enhanced} parameter
+     * must be true.
+     *
+     * @param source the snippet's masked source code
+     * @param wtype variable's denotable type suitable for field declaration
+     * @param brackets any [] that should be appended to the type
+     * @param rname range in source that denotes the name of the
+     * @param winit Initializer or null
+     * @param enhanced if the real inferred type of the variable is potentially
+     *                 non-denotable, this must be true
+     * @return a Wrap that declares the given variable, potentially with
+     *         an initialization method
      */
     public static Wrap varWrap(String source, Wrap wtype, String brackets,
-                               Range rname, Wrap winit, Wrap anonDeclareWrap) {
+                               Range rname, Wrap winit, boolean enhanced,
+                               Wrap anonDeclareWrap) {
         RangeWrap wname = new RangeWrap(source, rname);
-        Wrap wVarDecl = new VarDeclareWrap(wtype, brackets, wname);
+        List<Object> components = new ArrayList<>();
+        components.add(new VarDeclareWrap(wtype, brackets, wname));
         Wrap wmeth;
 
         if (winit == null) {
             wmeth = new CompoundWrap(new NoWrap(" "), "   return null;\n");
         } else {
-        // int x = y
-            // int x_ = y; return x = x_;
-            // decl + "_ = " + init ; + "return " + name + "=" + name + "_ ;"
-            wmeth = new CompoundWrap(
-                    wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
-                    "        return ", wname, " = ", wname, "_;\n"
-            );
+            // int x = y
+            if (enhanced) {
+                // private static <Z> Z do_itAux() {
+                //     wtype x_ = y;
+                //     @SuppressWarnings("unchecked")
+                //     Z x__ = (Z) x_;
+                //     return x__;
+                // }
+                // in do_it method:
+                //return do_itAux();
+                Wrap waux = new CompoundWrap(
+                        "    private static <Z> Z ", DOIT_METHOD_NAME + "Aux", "() throws Throwable {\n",
+                        wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
+                        "        @SuppressWarnings(\"unchecked\") Z ", wname, "__ = (Z)", wname, "_;\n",
+                        "        return ", wname, "__;\n",
+                        "}"
+                );
+                components.add(waux);
+                wmeth = new CompoundWrap(
+                        "        return ", wname, " = ", DOIT_METHOD_NAME + "Aux", "();\n"
+                );
+            } else {
+                // int x_ = y; return x = x_;
+                // decl + "_ = " + init ; + "return " + name + "= " + name + "_ ;"
+                wmeth = new CompoundWrap(
+                        wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
+                        "        return ", wname, " = ", wname, "_;\n"
+                );
+            }
         }
-        Wrap wInitMeth = new DoitMethodWrap(wmeth);
-        return anonDeclareWrap != null ? new CompoundWrap(wVarDecl, wInitMeth, anonDeclareWrap)
-                                       : new CompoundWrap(wVarDecl, wInitMeth);
+        components.add(new DoitMethodWrap(wmeth));
+        if (anonDeclareWrap != null) {
+            components.add(anonDeclareWrap);
+        }
+        return new CompoundWrap(components.toArray());
     }
 
-    public static Wrap tempVarWrap(String source, String typename, String name) {
+    public static Wrap tempVarWrap(String source, String typename, String name, Wrap anonDeclareWrap) {
         RangeWrap winit = new NoWrap(source);
         // y
         // return $1 = y;
@@ -105,7 +146,8 @@
         Wrap wInitMeth = new DoitMethodWrap(wmeth);
 
         String varDecl = "    public static\n    " + typename + " " + name + ";\n";
-        return new CompoundWrap(varDecl, wInitMeth);
+        return anonDeclareWrap != null ? new CompoundWrap(varDecl, wInitMeth, anonDeclareWrap)
+                                       : new CompoundWrap(varDecl, wInitMeth);
     }
 
     public static Wrap simpleWrap(String source) {
--- a/test/langtools/jdk/jshell/InaccessibleExpressionTest.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/test/langtools/jdk/jshell/InaccessibleExpressionTest.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -22,7 +22,7 @@
  */
 
 /*
- * @test 8190939
+ * @test 8190939 8191842
  * @summary test expressions whose type is inaccessible
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -86,6 +86,8 @@
         assertEval(list.name() + ".size()", "0");
         VarSnippet one = varKey(assertEval("priv()", "One"));
         assertEquals(one.typeName(), "priv.GetPriv.Count");
+        assertEval("var v = down();", "Packp");
+        assertDeclareFail("v.toString()", "compiler.err.not.def.access.class.intf.cant.access");
     }
 
     public void testInternal() {
--- a/test/langtools/jdk/jshell/ToolSimpleTest.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/test/langtools/jdk/jshell/ToolSimpleTest.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103  8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979
+ * @bug 8153716 8143955 8151754 8150382 8153920 8156910 8131024 8160089 8153897 8167128 8154513 8170015 8170368 8172102 8172103  8165405 8173073 8173848 8174041 8173916 8174028 8174262 8174797 8177079 8180508 8177466 8172154 8192979 8191842
  * @summary Simple jshell tool tests
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
@@ -786,7 +786,11 @@
                 (a) -> assertCommandOutputContains(a, "var r1 = new Object() {}", "r1"),
                 (a) -> assertCommandOutputContains(a, "/vars r1", "|    <anonymous class extending Object> r1 = "),
                 (a) -> assertCommandOutputContains(a, "var r2 = new Runnable() { public void run() { } }", "r2"),
-                (a) -> assertCommandOutputContains(a, "/vars r2", "|    <anonymous class implementing Runnable> r2 = ")
+                (a) -> assertCommandOutputContains(a, "/vars r2", "|    <anonymous class implementing Runnable> r2 = "),
+                (a) -> assertCommandOutputContains(a, "import java.util.stream.*;", ""),
+                (a) -> assertCommandOutputContains(a, "var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());",
+                                                      "list"),
+                (a) -> assertCommandOutputContains(a, "/vars list", "|    List<<anonymous class extending Object>> list = ")
         );
     }
 }
--- a/test/langtools/jdk/jshell/TypeNameTest.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/test/langtools/jdk/jshell/TypeNameTest.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8144903 8171981 8191802
+ * @bug 8144903 8171981 8191802 8191842
  * @summary Tests for determining the type from the expression
  * @build KullaTesting TestingInputStream
  * @run testng TypeNameTest
@@ -38,8 +38,12 @@
 
 
     private void assertType(String expr, String type) {
+        assertType(expr, type, type);
+    }
+
+    private void assertType(String expr, String type, String inferType) {
         assertEquals(varKey(assertEval(expr)).typeName(), type);
-        assertInferredType(expr, type);
+        assertInferredType(expr, inferType);
     }
 
     public void testTypeInference() {
@@ -58,7 +62,7 @@
         assertType("d.getS()", "D<?>");
         assertType("null", "Object");
         assertType("Class.forName( \"java.util.ArrayList\" )", "Class<?>");
-        assertType("new ArrayList<Boolean>() {}", "ArrayList<Boolean>");
+        assertType("new ArrayList<Boolean>() {}", "<anonymous class extending ArrayList<Boolean>>", "ArrayList<Boolean>");
         assertType("new ArrayList<String>().stream()", "java.util.stream.Stream<String>");
         assertType("Arrays.asList( 1, 2, 3)", "List<Integer>");
         assertType("new ArrayList().getClass().getClass()", "Class<? extends Class>");
@@ -188,7 +192,7 @@
         assertType("arrayOf(99)[0]", "Integer");
 
         assertEval("<Z> Z choose(Z z1, Z z2) { return z1; }");
-        assertType("choose(1, 1L);", "Object");
+        assertType("choose(1, 1L);", "Number&Comparable<? extends Number&Comparable<?>>", "Object");
     }
 
     public void testVariableTypeName() {
@@ -215,7 +219,7 @@
     public void testAnonymousClassName() {
         assertEval("class C {}");
         assertType("new C();", "C");
-        assertType("new C() { int x; };", "C");
+        assertType("new C() { int x; };", "<anonymous class extending C>", "C");
     }
 
     public void testCapturedTypeName() {
@@ -243,7 +247,7 @@
         assertType("test1.get()", "CharSequence");
         assertEval("class Test2<X extends Number & CharSequence> { public X get() { return null; } }");
         assertEval("Test2<?> test2 = new Test2<>();");
-        assertType("test2.get()", "Object");
+        assertType("test2.get()", "Number&CharSequence", "Object");
         assertEval("class Test3<T> { T[][] get() { return null; } }");
         assertEval("Test3<? extends String> test3 = new Test3<>();");
         assertType("test3.get()", "String[][]");
--- a/test/langtools/jdk/jshell/VariablesTest.java	Fri Jan 19 15:21:49 2018 +0800
+++ b/test/langtools/jdk/jshell/VariablesTest.java	Fri Jan 19 17:11:52 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -23,12 +23,15 @@
 
 /*
  * @test
- * @bug 8144903 8177466
+ * @bug 8144903 8177466 8191842
  * @summary Tests for EvaluationState.variables
- * @build KullaTesting TestingInputStream ExpectedDiagnostic
+ * @library /tools/lib
+ * @build Compiler KullaTesting TestingInputStream ExpectedDiagnostic
  * @run testng VariablesTest
  */
 
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.List;
 import javax.tools.Diagnostic;
 
@@ -37,6 +40,7 @@
 import jdk.jshell.VarSnippet;
 import jdk.jshell.Snippet.SubKind;
 import jdk.jshell.SnippetEvent;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import static java.util.stream.Collectors.toList;
@@ -362,5 +366,157 @@
         assertEval("class O2 { public class Inner { public Inner(int i) { } public String test() { return \"good\"; } } }");
         assertEval("var r5 = new O2().new Inner(1) { public String get() { return \"good\"; } };");
         assertEval("r5.get()", "\"good\"");
+        assertEval("<Z> Z identity(Z z) { return z; }");
+        assertEval("var r6 = identity(new Object() { String s = \"good\"; });");
+        assertEval("r6.s", "\"good\"");
+        assertEval("interface I<B, C> { C get(B b); }");
+        assertEval("<A, B, C> C cascade(A a, I<A, B> c1, I<B, C> c2) { return c2.get(c1.get(a)); }");
+        assertEval("var r7 = cascade(\"good\", a -> new Object() { String s = a; }, b -> new java.util.ArrayList<String>(5) { String s = b.s; });");
+        assertEval("r7.s", "\"good\"");
+        assertEval("var r8 = cascade(\"good\", a -> new Object() { String s = a; public String getS() { return s; } }, b -> new java.util.ArrayList<String>(5) { String s = b.getS(); public String getS() { return s; } });");
+        assertEval("r8.getS()", "\"good\"");
+        assertEval("var r9 = new Object() { class T { class Inner { public String g() { return outer(); } } public String outer() { return \"good\"; } public String test() { return new Inner() {}.g(); } } public String test() { return new T().test(); } };");
+        assertEval("r9.test()", "\"good\"");
+        assertEval("var nested1 = new Object() { class N { public String get() { return \"good\"; } } };");
+        assertEval("nested1.new N().get()", "\"good\"");
+        assertEval("var nested2 = cascade(\"good\", a -> new Object() { abstract class G { abstract String g(); } G g = new G() { String g() { return a; } }; }, b -> new java.util.ArrayList<String>(5) { String s = b.g.g(); });");
+        assertEval("nested2.s", "\"good\"");
+        assertEval("<A, B> B convert(A a, I<A, B> c) { return c.get(a); }");
+        assertEval("var r10 = convert(\"good\", a -> new api.C(12) { public String val = \"\" + i + s + l + a; } );");
+        assertEval("r10.val", "\"12empty[empty]good\"");
+        assertEval("var r11 = convert(\"good\", a -> new api.C(\"a\") { public String val = \"\" + i + s + l + a; } );");
+        assertEval("r11.val", "\"3a[empty]good\"");
+        assertEval("import api.C;");
+        assertEval("var r12 = convert(\"good\", a -> new C(java.util.List.of(\"a\")) { public String val = \"\" + i + s + l + a; } );");
+        assertEval("r12.val", "\"4empty[a]good\"");
+        assertEval("var r13 = convert(\"good\", a -> new api.G<String>(java.util.List.of(\"b\")) { public String val = \"\" + l + a; } );");
+        assertEval("r13.val", "\"[b]good\"");
+        assertEval("var r14 = convert(\"good\", a -> new api.J<String>() { public java.util.List<String> get() { return java.util.List.of(a, \"c\"); } } );");
+        assertEval("r14.get()", "[good, c]");
+        assertEval("var r15a = new java.util.ArrayList<String>();");
+        assertEval("r15a.add(\"a\");");
+        assertEval("var r15b = r15a.get(0);");
+        assertEval("r15b", "\"a\"");
+    }
+
+    public void test8191842() {
+        assertEval("import java.util.stream.*;");
+        assertEval("var list = Stream.of(1, 2, 3).map(j -> new Object() { int i = j; }).collect(Collectors.toList());");
+        assertEval("list.stream().map(a -> String.valueOf(a.i)).collect(Collectors.joining(\", \"));", "\"1, 2, 3\"");
+    }
+
+    public void lvtiRecompileDependentsWithIntersectionTypes() {
+        assertEval("<Z extends Runnable & CharSequence> Z get1() { return null; }", added(VALID));
+        VarSnippet var = varKey(assertEval("var i1 = get1();", added(VALID)));
+        assertEval("import java.util.stream.*;", added(VALID),
+                                                 ste(var, VALID, VALID, true, MAIN_SNIPPET));
+        assertEval("void t1() { i1.run(); i1.length(); }", added(VALID));
+    }
+
+    public void arrayInit() {
+        assertEval("int[] d = {1, 2, 3};");
+    }
+
+    public void testAnonymousVar() {
+        assertEval("new Object() { public String get() { return \"a\"; } }");
+        assertEval("$1.get()", "\"a\"");
+    }
+
+    public void testIntersectionVar() {
+        assertEval("<Z extends Runnable & CharSequence> Z get() { return null; }", added(VALID));
+        assertEval("get();", added(VALID));
+        assertEval("void t1() { $1.run(); $1.length(); }", added(VALID));
+    }
+
+    public void multipleCaptures() {
+        assertEval("class D { D(int foo, String bar) { this.foo = foo; this.bar = bar; } int foo; String bar; } ");
+        assertEval("var d = new D(34, \"hi\") { String z = foo + bar; };");
+        assertEval("d.z", "\"34hi\"");
+    }
+
+    public void multipleAnonymous() {
+        VarSnippet v1 = varKey(assertEval("new Object() { public int i = 42; public int i1 = i; public int m1() { return i1; } };"));
+        VarSnippet v2 = varKey(assertEval("new Object() { public int i = 42; public int i2 = i; public int m2() { return i2; } };"));
+        assertEval(v1.name() + ".i", "42");
+        assertEval(v1.name() + ".i1", "42");
+        assertEval(v1.name() + ".m1()", "42");
+        assertDeclareFail(v1.name() + ".i2",
+                          new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 5, 2,
+                                                 -1, -1, Diagnostic.Kind.ERROR));
+        assertEval(v2.name() + ".i", "42");
+        assertEval(v2.name() + ".i2", "42");
+        assertEval(v2.name() + ".m2()", "42");
+        assertDeclareFail(v2.name() + ".i1",
+                          new ExpectedDiagnostic("compiler.err.cant.resolve.location", 0, 5, 2,
+                                                 -1, -1, Diagnostic.Kind.ERROR));
+    }
+
+    public void displayName() {
+        assertVarDisplayName("var v1 = 234;", "int");
+        assertVarDisplayName("var v2 = new int[] {234};", "int[]");
+        assertEval("<Z extends Runnable & CharSequence> Z get() { return null; }", added(VALID));
+        assertVarDisplayName("var v3 = get();", "CharSequence&Runnable");
+        assertVarDisplayName("var v4a = new java.util.ArrayList<String>();", "java.util.ArrayList<String>");
+        assertEval("v4a.add(\"a\");");
+        assertVarDisplayName("var v4b = v4a.get(0);", "String");
+        assertVarDisplayName("var v5 = new Object() { };", "<anonymous class extending Object>");
+        assertVarDisplayName("var v6 = new Runnable() { public void run() { } };", "<anonymous class implementing Runnable>");
+    }
+
+    private void assertVarDisplayName(String var, String typeName) {
+        assertEquals(varKey(assertEval(var)).typeName(), typeName);
+    }
+
+    @BeforeMethod
+    @Override
+    public void setUp() {
+        Path path = Paths.get("cp");
+        Compiler compiler = new Compiler();
+        compiler.compile(path,
+                "package api;\n" +
+                "\n" +
+                "import java.util.List;\n" +
+                "\n" +
+                "public class C {\n" +
+                "   public int i;\n" +
+                "   public String s;\n" +
+                "   public List<String> l;\n" +
+                "   public C(int i) {\n" +
+                "       this.i = i;\n" +
+                "       this.s = \"empty\";\n" +
+                "       this.l = List.of(\"empty\");\n" +
+                "   }\n" +
+                "   public C(String s) {\n" +
+                "       this.i = 3;\n" +
+                "       this.s = s;\n" +
+                "       this.l = List.of(\"empty\");\n" +
+                "   }\n" +
+                "   public C(List<String> l) {\n" +
+                "       this.i = 4;\n" +
+                "       this.s = \"empty\";\n" +
+                "       this.l = l;\n" +
+                "   }\n" +
+                "}\n",
+                "package api;\n" +
+                "\n" +
+                "import java.util.List;\n" +
+                "\n" +
+                "public class G<T> {\n" +
+                "   public List<T> l;\n" +
+                "   public G(List<T> l) {\n" +
+                "       this.l = l;\n" +
+                "   }\n" +
+                "}\n",
+                "package api;\n" +
+                "\n" +
+                "import java.util.List;\n" +
+                "\n" +
+                "public interface J<T> {\n" +
+                "   public List<T> get();\n" +
+                "}\n");
+        String tpath = compiler.getPath(path).toString();
+        setUp(b -> b
+                .remoteVMOptions("--class-path", tpath)
+                .compilerOptions("--class-path", tpath));
     }
 }