src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
changeset 59285 7799a51dbe30
equal deleted inserted replaced
59284:88502b1cf76f 59285:7799a51dbe30
       
     1 /*
       
     2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.tools.javac.comp;
       
    27 
       
    28 import com.sun.tools.javac.code.Flags;
       
    29 import com.sun.tools.javac.code.Symbol.VarSymbol;
       
    30 import com.sun.tools.javac.code.Symtab;
       
    31 import com.sun.tools.javac.code.Type;
       
    32 import com.sun.tools.javac.code.Types;
       
    33 import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
       
    34 import com.sun.tools.javac.tree.JCTree;
       
    35 import com.sun.tools.javac.tree.JCTree.JCAssign;
       
    36 import com.sun.tools.javac.tree.JCTree.JCBinary;
       
    37 import com.sun.tools.javac.tree.JCTree.JCConditional;
       
    38 import com.sun.tools.javac.tree.JCTree.JCExpression;
       
    39 import com.sun.tools.javac.tree.JCTree.JCForLoop;
       
    40 import com.sun.tools.javac.tree.JCTree.JCIdent;
       
    41 import com.sun.tools.javac.tree.JCTree.JCIf;
       
    42 import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
       
    43 import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
       
    44 import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
       
    45 import com.sun.tools.javac.tree.JCTree.JCStatement;
       
    46 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
       
    47 import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
       
    48 import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
       
    49 import com.sun.tools.javac.tree.JCTree.Tag;
       
    50 import com.sun.tools.javac.tree.TreeMaker;
       
    51 import com.sun.tools.javac.tree.TreeTranslator;
       
    52 import com.sun.tools.javac.util.Assert;
       
    53 import com.sun.tools.javac.util.Context;
       
    54 import com.sun.tools.javac.util.List;
       
    55 import com.sun.tools.javac.util.ListBuffer;
       
    56 import com.sun.tools.javac.util.Log;
       
    57 import com.sun.tools.javac.util.Names;
       
    58 import com.sun.tools.javac.util.Options;
       
    59 
       
    60 import java.util.Map;
       
    61 import java.util.Map.Entry;
       
    62 import java.util.stream.Collectors;
       
    63 
       
    64 import com.sun.tools.javac.code.Symbol.MethodSymbol;
       
    65 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
       
    66 import static com.sun.tools.javac.code.TypeTag.BOT;
       
    67 import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
       
    68 import com.sun.tools.javac.jvm.Target;
       
    69 import com.sun.tools.javac.tree.JCTree.JCBlock;
       
    70 import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
       
    71 import com.sun.tools.javac.tree.JCTree.JCStatement;
       
    72 import com.sun.tools.javac.tree.JCTree.LetExpr;
       
    73 import com.sun.tools.javac.util.List;
       
    74 
       
    75 /**
       
    76  * This pass translates pattern-matching constructs, such as instanceof <pattern>.
       
    77  */
       
    78 public class TransPatterns extends TreeTranslator {
       
    79 
       
    80     protected static final Context.Key<TransPatterns> transPatternsKey = new Context.Key<>();
       
    81 
       
    82     public static TransPatterns instance(Context context) {
       
    83         TransPatterns instance = context.get(transPatternsKey);
       
    84         if (instance == null)
       
    85             instance = new TransPatterns(context);
       
    86         return instance;
       
    87     }
       
    88 
       
    89     private final Symtab syms;
       
    90     private final Types types;
       
    91     private final Operators operators;
       
    92     private final Log log;
       
    93     private final ConstFold constFold;
       
    94     private final Names names;
       
    95     private final Target target;
       
    96     private final MatchBindingsComputer matchBindingsComputer;
       
    97     private TreeMaker make;
       
    98 
       
    99     BindingContext bindingContext = new BindingContext() {
       
   100         @Override
       
   101         VarSymbol getBindingFor(BindingSymbol varSymbol) {
       
   102             return null;
       
   103         }
       
   104 
       
   105         @Override
       
   106         JCStatement decorateStatement(JCStatement stat) {
       
   107             return stat;
       
   108         }
       
   109 
       
   110         @Override
       
   111         JCExpression decorateExpression(JCExpression expr) {
       
   112             return expr;
       
   113         }
       
   114 
       
   115         @Override
       
   116         BindingContext pop() {
       
   117             //do nothing
       
   118             return this;
       
   119         }
       
   120 
       
   121         @Override
       
   122         boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
       
   123             return false;
       
   124         }
       
   125     };
       
   126 
       
   127     JCLabeledStatement pendingMatchLabel = null;
       
   128 
       
   129     boolean debugTransPatterns;
       
   130 
       
   131     private MethodSymbol currentMethodSym = null;
       
   132 
       
   133     protected TransPatterns(Context context) {
       
   134         context.put(transPatternsKey, this);
       
   135         syms = Symtab.instance(context);
       
   136         make = TreeMaker.instance(context);
       
   137         types = Types.instance(context);
       
   138         operators = Operators.instance(context);
       
   139         log = Log.instance(context);
       
   140         constFold = ConstFold.instance(context);
       
   141         names = Names.instance(context);
       
   142         target = Target.instance(context);
       
   143         matchBindingsComputer = MatchBindingsComputer.instance(context);
       
   144         debugTransPatterns = Options.instance(context).isSet("debug.patterns");
       
   145     }
       
   146 
       
   147     @Override
       
   148     public void visitTypeTest(JCInstanceOf tree) {
       
   149         if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) {
       
   150             //E instanceof T N
       
   151             //=>
       
   152             //(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))
       
   153             JCBindingPattern patt = (JCBindingPattern)tree.pattern;
       
   154             VarSymbol pattSym = patt.symbol;
       
   155             Type tempType = tree.expr.type.hasTag(BOT) ?
       
   156                     syms.objectType
       
   157                     : tree.expr.type;
       
   158             VarSymbol temp = new VarSymbol(pattSym.flags() | Flags.SYNTHETIC,
       
   159                     names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"),
       
   160                     tempType,
       
   161                     patt.symbol.owner);
       
   162             JCExpression translatedExpr = translate(tree.expr);
       
   163             Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types));
       
   164 
       
   165             result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
       
   166 
       
   167             VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol);
       
   168             if (bindingVar != null) {
       
   169                 JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
       
   170                         make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
       
   171                 result = makeBinary(Tag.AND, (JCExpression)result,
       
   172                         makeBinary(Tag.EQ, fakeInit, convert(make.Ident(temp), castTargetType)));
       
   173             }
       
   174             result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType);
       
   175             ((LetExpr) result).needsCond = true;
       
   176         } else {
       
   177             super.visitTypeTest(tree);
       
   178         }
       
   179     }
       
   180 
       
   181     @Override
       
   182     public void visitBinary(JCBinary tree) {
       
   183         List<BindingSymbol> matchBindings;
       
   184         switch (tree.getTag()) {
       
   185             case AND:
       
   186                 matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
       
   187                 break;
       
   188             case OR:
       
   189                 matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false);
       
   190                 break;
       
   191             default:
       
   192                 matchBindings = List.nil();
       
   193                 break;
       
   194         }
       
   195 
       
   196         bindingContext = new BasicBindingContext(matchBindings);
       
   197         try {
       
   198             super.visitBinary(tree);
       
   199             result = bindingContext.decorateExpression(tree);
       
   200         } finally {
       
   201             bindingContext.pop();
       
   202         }
       
   203     }
       
   204 
       
   205     @Override
       
   206     public void visitConditional(JCConditional tree) {
       
   207         bindingContext = new BasicBindingContext(
       
   208                 matchBindingsComputer.getMatchBindings(tree.cond, true)
       
   209                         .appendList(matchBindingsComputer.getMatchBindings(tree.cond, false)));
       
   210         try {
       
   211             super.visitConditional(tree);
       
   212             result = bindingContext.decorateExpression(tree);
       
   213         } finally {
       
   214             bindingContext.pop();
       
   215         }
       
   216     }
       
   217 
       
   218     @Override
       
   219     public void visitIf(JCIf tree) {
       
   220         bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
       
   221         try {
       
   222             super.visitIf(tree);
       
   223             result = bindingContext.decorateStatement(tree);
       
   224         } finally {
       
   225             bindingContext.pop();
       
   226         }
       
   227     }
       
   228 
       
   229     @Override
       
   230     public void visitForLoop(JCForLoop tree) {
       
   231         bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
       
   232         try {
       
   233             super.visitForLoop(tree);
       
   234             result = bindingContext.decorateStatement(tree);
       
   235         } finally {
       
   236             bindingContext.pop();
       
   237         }
       
   238     }
       
   239 
       
   240     @Override
       
   241     public void visitWhileLoop(JCWhileLoop tree) {
       
   242         bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
       
   243         try {
       
   244             super.visitWhileLoop(tree);
       
   245             result = bindingContext.decorateStatement(tree);
       
   246         } finally {
       
   247             bindingContext.pop();
       
   248         }
       
   249     }
       
   250 
       
   251     @Override
       
   252     public void visitDoLoop(JCDoWhileLoop tree) {
       
   253         bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
       
   254         try {
       
   255             super.visitDoLoop(tree);
       
   256             result = bindingContext.decorateStatement(tree);
       
   257         } finally {
       
   258             bindingContext.pop();
       
   259         }
       
   260     }
       
   261 
       
   262     @Override
       
   263     public void visitMethodDef(JCMethodDecl tree) {
       
   264         MethodSymbol prevMethodSym = currentMethodSym;
       
   265         try {
       
   266             currentMethodSym = tree.sym;
       
   267             super.visitMethodDef(tree);
       
   268         } finally {
       
   269             currentMethodSym = prevMethodSym;
       
   270         }
       
   271     }
       
   272 
       
   273     @Override
       
   274     public void visitIdent(JCIdent tree) {
       
   275         VarSymbol bindingVar = null;
       
   276         if ((tree.sym.flags() & Flags.MATCH_BINDING) != 0) {
       
   277             bindingVar = bindingContext.getBindingFor((BindingSymbol)tree.sym);
       
   278         }
       
   279         if (bindingVar == null) {
       
   280             super.visitIdent(tree);
       
   281         } else {
       
   282             result = make.at(tree.pos).Ident(bindingVar);
       
   283         }
       
   284     }
       
   285 
       
   286     @Override
       
   287     public void visitBlock(JCBlock tree) {
       
   288         ListBuffer<JCStatement> statements = new ListBuffer<>();
       
   289         bindingContext = new BasicBindingContext(List.nil()) {
       
   290             boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
       
   291                 //{
       
   292                 //    if (E instanceof T N) {
       
   293                 //        return ;
       
   294                 //    }
       
   295                 //    //use of N:
       
   296                 //}
       
   297                 //=>
       
   298                 //{
       
   299                 //    T N;
       
   300                 //    if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
       
   301                 //        return ;
       
   302                 //    }
       
   303                 //    //use of N:
       
   304                 //}
       
   305                 hoistedVarMap.put(binding, var.sym);
       
   306                 statements.append(var);
       
   307                 return true;
       
   308             }
       
   309         };
       
   310         try {
       
   311             for (List<JCStatement> l = tree.stats; l.nonEmpty(); l = l.tail) {
       
   312                 statements.append(translate(l.head));
       
   313             }
       
   314 
       
   315             tree.stats = statements.toList();
       
   316             result = tree;
       
   317         } finally {
       
   318             bindingContext.pop();
       
   319         }
       
   320     }
       
   321 
       
   322     public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
       
   323         try {
       
   324             this.make = make;
       
   325             translate(cdef);
       
   326         } finally {
       
   327             // note that recursive invocations of this method fail hard
       
   328             this.make = null;
       
   329         }
       
   330 
       
   331         return cdef;
       
   332     }
       
   333 
       
   334     /** Make an instanceof expression.
       
   335      *  @param lhs      The expression.
       
   336      *  @param type     The type to be tested.
       
   337      */
       
   338 
       
   339     JCInstanceOf makeTypeTest(JCExpression lhs, JCExpression type) {
       
   340         JCInstanceOf tree = make.TypeTest(lhs, type);
       
   341         tree.type = syms.booleanType;
       
   342         return tree;
       
   343     }
       
   344 
       
   345     /** Make an attributed binary expression (copied from Lower).
       
   346      *  @param optag    The operators tree tag.
       
   347      *  @param lhs      The operator's left argument.
       
   348      *  @param rhs      The operator's right argument.
       
   349      */
       
   350     JCBinary makeBinary(JCTree.Tag optag, JCExpression lhs, JCExpression rhs) {
       
   351         JCBinary tree = make.Binary(optag, lhs, rhs);
       
   352         tree.operator = operators.resolveBinary(tree, optag, lhs.type, rhs.type);
       
   353         tree.type = tree.operator.type.getReturnType();
       
   354         return tree;
       
   355     }
       
   356 
       
   357     JCExpression convert(JCExpression expr, Type target) {
       
   358         JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr);
       
   359         result.type = target;
       
   360         return result;
       
   361     }
       
   362 
       
   363     private List<BindingSymbol> getMatchBindings(JCExpression cond) {
       
   364         return matchBindingsComputer.getMatchBindings(cond, true)
       
   365                         .appendList(matchBindingsComputer.getMatchBindings(cond, false));
       
   366     }
       
   367     abstract class BindingContext {
       
   368         abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
       
   369         abstract JCStatement decorateStatement(JCStatement stat);
       
   370         abstract JCExpression decorateExpression(JCExpression expr);
       
   371         abstract BindingContext pop();
       
   372         abstract boolean tryPrepend(BindingSymbol binding, JCVariableDecl var);
       
   373     }
       
   374 
       
   375     class BasicBindingContext extends BindingContext {
       
   376         List<BindingSymbol> matchBindings;
       
   377         Map<BindingSymbol, VarSymbol> hoistedVarMap;
       
   378         BindingContext parent;
       
   379 
       
   380         public BasicBindingContext(List<BindingSymbol> matchBindings) {
       
   381             this.matchBindings = matchBindings;
       
   382             this.parent = bindingContext;
       
   383             this.hoistedVarMap = matchBindings.stream()
       
   384                     .filter(v -> parent.getBindingFor(v) == null)
       
   385                     .collect(Collectors.toMap(v -> v, v -> {
       
   386                         VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner);
       
   387                         res.setTypeAttributes(v.getRawTypeAttributes());
       
   388                         return res;
       
   389                     }));
       
   390         }
       
   391 
       
   392         @Override
       
   393         VarSymbol getBindingFor(BindingSymbol varSymbol) {
       
   394             VarSymbol res = parent.getBindingFor(varSymbol);
       
   395             if (res != null) {
       
   396                 return res;
       
   397             }
       
   398             return hoistedVarMap.entrySet().stream()
       
   399                     .filter(e -> e.getKey().isAliasFor(varSymbol))
       
   400                     .findFirst()
       
   401                     .map(e -> e.getValue()).orElse(null);
       
   402         }
       
   403 
       
   404         @Override
       
   405         JCStatement decorateStatement(JCStatement stat) {
       
   406             if (hoistedVarMap.isEmpty()) return stat;
       
   407             //if (E instanceof T N) {
       
   408             //     //use N
       
   409             //}
       
   410             //=>
       
   411             //{
       
   412             //    T N;
       
   413             //    if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
       
   414             //        //use N
       
   415             //    }
       
   416             //}
       
   417             ListBuffer<JCStatement> stats = new ListBuffer<>();
       
   418             for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) {
       
   419                 JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue());
       
   420                 if (!e.getKey().isPreserved() ||
       
   421                     !parent.tryPrepend(e.getKey(), decl)) {
       
   422                     stats.add(decl);
       
   423                 }
       
   424             }
       
   425             if (stats.nonEmpty()) {
       
   426                 stats.add(stat);
       
   427                 stat = make.at(stat.pos).Block(0, stats.toList());
       
   428             }
       
   429             return stat;
       
   430         }
       
   431 
       
   432         @Override
       
   433         JCExpression decorateExpression(JCExpression expr) {
       
   434             //E instanceof T N && /*use of N*/
       
   435             //=>
       
   436             //(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/)
       
   437             for (VarSymbol vsym : hoistedVarMap.values()) {
       
   438                 expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type);
       
   439             }
       
   440             return expr;
       
   441         }
       
   442 
       
   443         @Override
       
   444         BindingContext pop() {
       
   445             return bindingContext = parent;
       
   446         }
       
   447 
       
   448         @Override
       
   449         boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
       
   450             return false;
       
   451         }
       
   452 
       
   453         private JCVariableDecl makeHoistedVarDecl(int pos, VarSymbol varSymbol) {
       
   454             return make.at(pos).VarDef(varSymbol, null);
       
   455         }
       
   456     }
       
   457 }