src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java
changeset 48610 a587f95313f1
parent 47350 d65c3b21081c
child 49092 6dc5e0cdb44c
--- 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 {}
+    }
+
 }