1 /* 2 * Copyright (c) 2017, 2020, 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; 30 import com.sun.tools.javac.code.Symbol.BindingSymbol; 31 import com.sun.tools.javac.code.Symbol.VarSymbol; 32 import com.sun.tools.javac.code.Symtab; 33 import com.sun.tools.javac.code.Type; 34 import com.sun.tools.javac.code.Types; 35 import com.sun.tools.javac.tree.JCTree; 36 import com.sun.tools.javac.tree.JCTree.JCAssign; 37 import com.sun.tools.javac.tree.JCTree.JCBinary; 38 import com.sun.tools.javac.tree.JCTree.JCConditional; 39 import com.sun.tools.javac.tree.JCTree.JCExpression; 40 import com.sun.tools.javac.tree.JCTree.JCForLoop; 41 import com.sun.tools.javac.tree.JCTree.JCIdent; 42 import com.sun.tools.javac.tree.JCTree.JCIf; 43 import com.sun.tools.javac.tree.JCTree.JCInstanceOf; 44 import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; 45 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 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.ListBuffer; 55 import com.sun.tools.javac.util.Log; 56 import com.sun.tools.javac.util.Names; 57 import com.sun.tools.javac.util.Options; 58 59 import java.util.Map; 60 import java.util.Map.Entry; 61 62 import com.sun.tools.javac.code.Symbol.MethodSymbol; 63 import static com.sun.tools.javac.code.TypeTag.BOT; 64 import com.sun.tools.javac.jvm.Target; 65 import com.sun.tools.javac.tree.JCTree; 66 import com.sun.tools.javac.tree.JCTree.JCBlock; 67 import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; 68 import com.sun.tools.javac.tree.JCTree.JCLambda; 69 import com.sun.tools.javac.tree.JCTree.JCStatement; 70 import com.sun.tools.javac.tree.JCTree.LetExpr; 71 import com.sun.tools.javac.util.List; 72 import java.util.HashMap; 73 74 /** 75 * This pass translates pattern-matching constructs, such as instanceof <pattern>. 76 */ 77 public class TransPatterns extends TreeTranslator { 78 79 protected static final Context.Key<TransPatterns> transPatternsKey = new Context.Key<>(); 80 81 public static TransPatterns instance(Context context) { 82 TransPatterns instance = context.get(transPatternsKey); 83 if (instance == null) 84 instance = new TransPatterns(context); 85 return instance; 86 } 87 88 private final Symtab syms; 89 private final Types types; 90 private final Operators operators; 91 private final Log log; 92 private final ConstFold constFold; 93 private final Names names; 94 private final Target target; 95 private TreeMaker make; 96 97 BindingContext bindingContext = new BindingContext() { 98 @Override 99 VarSymbol bindingDeclared(BindingSymbol varSymbol) { 100 return null; 101 } 102 103 @Override 104 VarSymbol getBindingFor(BindingSymbol varSymbol) { 105 return null; 106 } 107 108 @Override 109 JCStatement decorateStatement(JCStatement stat) { 110 return stat; 111 } 112 113 @Override 114 JCExpression decorateExpression(JCExpression expr) { 115 return expr; 116 } 117 118 @Override 119 BindingContext pop() { 120 //do nothing 121 return this; 122 } 123 124 @Override 125 boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { 126 return false; 127 } 128 }; 129 130 JCLabeledStatement pendingMatchLabel = null; 131 132 boolean debugTransPatterns; 133 134 private MethodSymbol currentMethodSym = null; 135 136 protected TransPatterns(Context context) { 137 context.put(transPatternsKey, this); 138 syms = Symtab.instance(context); 139 make = TreeMaker.instance(context); 140 types = Types.instance(context); 141 operators = Operators.instance(context); 142 log = Log.instance(context); 143 constFold = ConstFold.instance(context); 144 names = Names.instance(context); 145 target = Target.instance(context); 146 debugTransPatterns = Options.instance(context).isSet("debug.patterns"); 147 } 148 149 @Override 150 public void visitTypeTest(JCInstanceOf tree) { 151 if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) { 152 //E instanceof T N 153 //=> 154 //(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) 155 JCBindingPattern patt = (JCBindingPattern)tree.pattern; 156 VarSymbol pattSym = patt.symbol; 157 Type tempType = tree.expr.type.hasTag(BOT) ? 158 syms.objectType 159 : tree.expr.type; 160 VarSymbol temp = new VarSymbol(pattSym.flags() | Flags.SYNTHETIC, 161 names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"), 162 tempType, 163 patt.symbol.owner); 164 JCExpression translatedExpr = translate(tree.expr); 165 Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types)); 166 167 result = makeTypeTest(make.Ident(temp), make.Type(castTargetType)); 168 169 VarSymbol bindingVar = bindingContext.bindingDeclared(patt.symbol); 170 if (bindingVar != null) { //TODO: cannot be null here? 171 JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign( 172 make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types)); 173 result = makeBinary(Tag.AND, (JCExpression)result, 174 makeBinary(Tag.EQ, fakeInit, convert(make.Ident(temp), castTargetType))); 175 } 176 result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType); 177 ((LetExpr) result).needsCond = true; 178 } else { 179 super.visitTypeTest(tree); 180 } 181 } 182 183 @Override 184 public void visitBinary(JCBinary tree) { 185 bindingContext = new BasicBindingContext(); 186 try { 187 super.visitBinary(tree); 188 result = bindingContext.decorateExpression(tree); 189 } finally { 190 bindingContext.pop(); 191 } 192 } 193 194 @Override 195 public void visitConditional(JCConditional tree) { 196 bindingContext = new BasicBindingContext(); 197 try { 198 super.visitConditional(tree); 199 result = bindingContext.decorateExpression(tree); 200 } finally { 201 bindingContext.pop(); 202 } 203 } 204 205 @Override 206 public void visitIf(JCIf tree) { 207 bindingContext = new BasicBindingContext(); 208 try { 209 super.visitIf(tree); 210 result = bindingContext.decorateStatement(tree); 211 } finally { 212 bindingContext.pop(); 213 } 214 } 215 216 @Override 217 public void visitForLoop(JCForLoop tree) { 218 bindingContext = new BasicBindingContext(); 219 try { 220 super.visitForLoop(tree); 221 result = bindingContext.decorateStatement(tree); 222 } finally { 223 bindingContext.pop(); 224 } 225 } 226 227 @Override 228 public void visitWhileLoop(JCWhileLoop tree) { 229 bindingContext = new BasicBindingContext(); 230 try { 231 super.visitWhileLoop(tree); 232 result = bindingContext.decorateStatement(tree); 233 } finally { 234 bindingContext.pop(); 235 } 236 } 237 238 @Override 239 public void visitDoLoop(JCDoWhileLoop tree) { 240 bindingContext = new BasicBindingContext(); 241 try { 242 super.visitDoLoop(tree); 243 result = bindingContext.decorateStatement(tree); 244 } finally { 245 bindingContext.pop(); 246 } 247 } 248 249 @Override 250 public void visitMethodDef(JCMethodDecl tree) { 251 MethodSymbol prevMethodSym = currentMethodSym; 252 try { 253 currentMethodSym = tree.sym; 254 super.visitMethodDef(tree); 255 } finally { 256 currentMethodSym = prevMethodSym; 257 } 258 } 259 260 @Override 261 public void visitIdent(JCIdent tree) { 262 VarSymbol bindingVar = null; 263 if ((tree.sym.flags() & Flags.MATCH_BINDING) != 0) { 264 bindingVar = bindingContext.getBindingFor((BindingSymbol)tree.sym); 265 } 266 if (bindingVar == null) { 267 super.visitIdent(tree); 268 } else { 269 result = make.at(tree.pos).Ident(bindingVar); 270 } 271 } 272 273 @Override 274 public void visitBlock(JCBlock tree) { 275 ListBuffer<JCStatement> statements = new ListBuffer<>(); 276 bindingContext = new BindingDeclarationFenceBindingContext() { 277 boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { 278 //{ 279 // if (E instanceof T N) { 280 // return ; 281 // } 282 // //use of N: 283 //} 284 //=> 285 //{ 286 // T N; 287 // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) { 288 // return ; 289 // } 290 // //use of N: 291 //} 292 hoistedVarMap.put(binding, var.sym); 293 statements.append(var); 294 return true; 295 } 296 }; 297 try { 298 for (List<JCStatement> l = tree.stats; l.nonEmpty(); l = l.tail) { 299 statements.append(translate(l.head)); 300 } 301 302 tree.stats = statements.toList(); 303 result = tree; 304 } finally { 305 bindingContext.pop(); 306 } 307 } 308 309 @Override 310 public void visitLambda(JCLambda tree) { 311 BindingContext prevContent = bindingContext; 312 try { 313 bindingContext = new BindingDeclarationFenceBindingContext(); 314 super.visitLambda(tree); 315 } finally { 316 bindingContext = prevContent; 317 } 318 } 319 320 public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 321 try { 322 this.make = make; 323 translate(cdef); 324 } finally { 325 // note that recursive invocations of this method fail hard 326 this.make = null; 327 } 328 329 return cdef; 330 } 331 332 /** Make an instanceof expression. 333 * @param lhs The expression. 334 * @param type The type to be tested. 335 */ 336 337 JCInstanceOf makeTypeTest(JCExpression lhs, JCExpression type) { 338 JCInstanceOf tree = make.TypeTest(lhs, type); 339 tree.type = syms.booleanType; 340 return tree; 341 } 342 343 /** Make an attributed binary expression (copied from Lower). 344 * @param optag The operators tree tag. 345 * @param lhs The operator's left argument. 346 * @param rhs The operator's right argument. 347 */ 348 JCBinary makeBinary(JCTree.Tag optag, JCExpression lhs, JCExpression rhs) { 349 JCBinary tree = make.Binary(optag, lhs, rhs); 350 tree.operator = operators.resolveBinary(tree, optag, lhs.type, rhs.type); 351 tree.type = tree.operator.type.getReturnType(); 352 return tree; 353 } 354 355 JCExpression convert(JCExpression expr, Type target) { 356 JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr); 357 result.type = target; 358 return result; 359 } 360 361 abstract class BindingContext { 362 abstract VarSymbol bindingDeclared(BindingSymbol varSymbol); 363 abstract VarSymbol getBindingFor(BindingSymbol varSymbol); 364 abstract JCStatement decorateStatement(JCStatement stat); 365 abstract JCExpression decorateExpression(JCExpression expr); 366 abstract BindingContext pop(); 367 abstract boolean tryPrepend(BindingSymbol binding, JCVariableDecl var); 368 } 369 370 class BasicBindingContext extends BindingContext { 371 Map<BindingSymbol, VarSymbol> hoistedVarMap; 372 BindingContext parent; 373 374 public BasicBindingContext() { 375 this.parent = bindingContext; 376 this.hoistedVarMap = new HashMap<>(); 377 } 378 379 @Override 380 VarSymbol bindingDeclared(BindingSymbol varSymbol) { 381 VarSymbol res = parent.bindingDeclared(varSymbol); 382 if (res == null) { 383 res = new VarSymbol(varSymbol.flags(), varSymbol.name, varSymbol.type, varSymbol.owner); 384 res.setTypeAttributes(varSymbol.getRawTypeAttributes()); 385 hoistedVarMap.put(varSymbol, res); 386 } 387 return res; 388 } 389 390 @Override 391 VarSymbol getBindingFor(BindingSymbol varSymbol) { 392 VarSymbol res = parent.getBindingFor(varSymbol); 393 if (res != null) { 394 return res; 395 } 396 return hoistedVarMap.entrySet().stream() 397 .filter(e -> e.getKey().isAliasFor(varSymbol)) 398 .findFirst() 399 .map(e -> e.getValue()).orElse(null); 400 } 401 402 @Override 403 JCStatement decorateStatement(JCStatement stat) { 404 if (hoistedVarMap.isEmpty()) return stat; 405 //if (E instanceof T N) { 406 // //use N 407 //} 408 //=> 409 //{ 410 // T N; 411 // if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) { 412 // //use N 413 // } 414 //} 415 ListBuffer<JCStatement> stats = new ListBuffer<>(); 416 for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) { 417 JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue()); 418 if (!e.getKey().isPreserved() || 419 !parent.tryPrepend(e.getKey(), decl)) { 420 stats.add(decl); 421 } 422 } 423 if (stats.nonEmpty()) { 424 stats.add(stat); 425 stat = make.at(stat.pos).Block(0, stats.toList()); 426 } 427 return stat; 428 } 429 430 @Override 431 JCExpression decorateExpression(JCExpression expr) { 432 //E instanceof T N && /*use of N*/ 433 //=> 434 //(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/) 435 for (VarSymbol vsym : hoistedVarMap.values()) { 436 expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type); 437 } 438 return expr; 439 } 440 441 @Override 442 BindingContext pop() { 443 return bindingContext = parent; 444 } 445 446 @Override 447 boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) { 448 return false; 449 } 450 451 private JCVariableDecl makeHoistedVarDecl(int pos, VarSymbol varSymbol) { 452 return make.at(pos).VarDef(varSymbol, null); 453 } 454 } 455 456 private class BindingDeclarationFenceBindingContext extends BasicBindingContext { 457 458 @Override 459 VarSymbol bindingDeclared(BindingSymbol varSymbol) { 460 return null; 461 } 462 463 } 464 }