1 /* 2 * Copyright (c) 2018, 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.jvm; 27 28 import com.sun.tools.javac.code.Flags; 29 import com.sun.tools.javac.code.Scope.LookupKind; 30 import com.sun.tools.javac.code.Symbol; 31 import com.sun.tools.javac.code.Symbol.MethodSymbol; 32 import com.sun.tools.javac.code.Symbol.VarSymbol; 33 import com.sun.tools.javac.code.Symtab; 34 import com.sun.tools.javac.code.Type; 35 import com.sun.tools.javac.code.Type.MethodType; 36 import com.sun.tools.javac.code.Types; 37 import com.sun.tools.javac.tree.JCTree; 38 import com.sun.tools.javac.tree.JCTree.JCAssign; 39 import com.sun.tools.javac.tree.JCTree.JCClassDecl; 40 import com.sun.tools.javac.tree.JCTree.JCExpression; 41 import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; 42 import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 43 import com.sun.tools.javac.tree.JCTree.JCIdent; 44 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 45 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 46 import com.sun.tools.javac.tree.JCTree.JCNewClass; 47 import com.sun.tools.javac.tree.JCTree.JCReturn; 48 import com.sun.tools.javac.tree.JCTree.JCStatement; 49 import com.sun.tools.javac.tree.TreeInfo; 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.Name; 56 import com.sun.tools.javac.util.Names; 57 58 import java.util.HashMap; 59 import java.util.Map; 60 61 import static com.sun.tools.javac.code.Flags.RECORD; 62 import static com.sun.tools.javac.code.Flags.STATIC; 63 import static com.sun.tools.javac.code.Flags.ABSTRACT; 64 import static com.sun.tools.javac.code.Flags.VALUE_CLASS; 65 import static com.sun.tools.javac.code.Kinds.Kind.MTH; 66 import static com.sun.tools.javac.code.Kinds.Kind.TYP; 67 import static com.sun.tools.javac.code.Kinds.Kind.VAR; 68 import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; 69 import static com.sun.tools.javac.tree.JCTree.Tag.EXEC; 70 import static com.sun.tools.javac.tree.JCTree.Tag.IDENT; 71 72 /** 73 * This pass translates value class constructors into static factory methods and patches up constructor 74 * calls to become invocations of those static factory methods. 75 * 76 * We get commissioned as a subpass of Gen. Constructor trees undergo plenty of change in Lower 77 * (enclosing instance injection, captured locals ...) and in Gen (instance field initialization, 78 * see normalizeDefs) and so it is most effective to wait until things reach a quiescent state 79 * before undertaking the tinkering that we do. 80 * 81 * See https://bugs.openjdk.java.net/browse/JDK-8198749 for the kind of transformations we do. 82 * 83 */ 84 public class TransValues extends TreeTranslator { 85 86 protected static final Context.Key<TransValues> transValueClass = new Context.Key<>(); 87 88 private Symtab syms; 89 private TreeMaker make; 90 private Types types; 91 private Names names; 92 93 /* Is an assignment undergoing translation just an assignment statement ? 94 Or is also a value ?? 95 */ 96 private boolean requireRVal; 97 98 // class currently undergoing translation. 99 private JCClassDecl currentClass; 100 101 // method currently undergoing translation. 102 private JCMethodDecl currentMethod; 103 104 // list of factories synthesized so far. 105 private List<JCTree> staticFactories; 106 107 // Map from constructor symbols to factory symbols. 108 private Map<MethodSymbol, MethodSymbol> init2factory = new HashMap<>(); 109 110 public static TransValues instance(Context context) { 111 TransValues instance = context.get(transValueClass); 112 if (instance == null) { 113 instance = new TransValues(context); 114 context.put(transValueClass, instance); 115 } 116 return instance; 117 } 118 119 protected TransValues(Context context) { 120 syms = Symtab.instance(context); 121 make = TreeMaker.instance(context); 122 types = Types.instance(context); 123 names = Names.instance(context); 124 } 125 126 @SuppressWarnings("unchecked") 127 public <T extends JCTree> T translate(T tree, boolean requireRVal) { 128 boolean priorRequireRVal = this.requireRVal; 129 try { 130 this.requireRVal = requireRVal; 131 if (tree == null) { 132 return null; 133 } else { 134 tree.accept(this); 135 JCTree tmpResult = this.result; 136 this.result = null; 137 return (T)tmpResult; // XXX cast 138 } 139 } finally { 140 this.requireRVal = priorRequireRVal; 141 } 142 } 143 144 @Override 145 public <T extends JCTree> T translate(T tree) { 146 return translate(tree, true); 147 } 148 149 public JCClassDecl translateTopLevelClass(JCClassDecl classDecl, TreeMaker make) { 150 try { 151 this.make = make; 152 translate(classDecl); 153 } finally { 154 // note that recursive invocations of this method fail hard 155 this.make = null; 156 } 157 init2factory = new HashMap<>(); 158 return classDecl; 159 } 160 161 @Override 162 public void visitClassDef(JCClassDecl classDecl) { 163 JCClassDecl previousClass = currentClass; 164 List<JCTree> previousFactories = staticFactories; 165 staticFactories = List.nil(); 166 currentClass = classDecl; 167 try { 168 super.visitClassDef(classDecl); 169 classDecl.defs = classDecl.defs.appendList(staticFactories); 170 staticFactories = List.nil(); 171 } 172 finally { 173 currentClass = previousClass; 174 staticFactories = previousFactories; 175 } 176 } 177 178 @Override 179 public void visitMethodDef(JCMethodDecl tree) { 180 JCMethodDecl previousMethod = currentMethod; 181 currentMethod = tree; 182 try { 183 if (transformToValueFactory()) { 184 185 // Mutate this value class constructor into an equivalent static factory 186 make.at(tree.pos()); 187 JCExpressionStatement exec = chainedConstructorCall(tree); 188 Assert.check(exec != null && TreeInfo.isSelfCall(exec)); 189 JCMethodInvocation call = (JCMethodInvocation) exec.expr; 190 191 /* Unlike the reference construction sequence where `this' is allocated ahead of time and 192 is passed as an argument into the <init> method, the value static factory must allocate the 193 instance that forms the `product' by itself. We do that by injecting a prologue here. 194 */ 195 VarSymbol product = currentMethod.factoryProduct = new VarSymbol(0, names.dollarValue, currentClass.sym.type, currentMethod.sym); // TODO: owner needs rewiring 196 JCExpression rhs; 197 198 final Name name = TreeInfo.name(call.meth); 199 MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth); 200 if (names._super.equals(name)) { // "initial" constructor. 201 // Synthesize code to allocate factory "product" via: V $this = V.default; 202 Assert.check(symbol.type.getParameterTypes().size() == 0); 203 final JCExpression type = make.Type(currentClass.type); 204 rhs = make.DefaultValue(type); 205 rhs.type = currentClass.type; 206 } else { 207 // This must be a chained call of form `this(args)'; Mutate it into a factory invocation i.e V $this = V.init(args); 208 Assert.check(TreeInfo.name(TreeInfo.firstConstructorCall(tree).meth) == names._this); 209 MethodSymbol factory = getValueObjectFactory(symbol); 210 final JCIdent ident = make.Ident(factory); 211 rhs = make.App(ident, call.args); 212 ((JCMethodInvocation)rhs).varargsElement = call.varargsElement; 213 } 214 215 /* The static factory product allocation prologue must precede any synthetic inits !!! 216 as these may reference `this' which gets pre-allocated for references but 217 not for value objects. 218 */ 219 JCStatement prologue = make.VarDef(product, rhs); 220 tree.body.stats = tree.body.stats.prepend(prologue).diff(List.of(exec)); 221 tree.body = translate(tree.body); 222 223 MethodSymbol factorySym = getValueObjectFactory(tree.sym); 224 currentMethod.setType(factorySym.type); 225 currentMethod.factoryProduct = product; 226 currentClass.sym.members().remove(tree.sym); 227 tree.sym = factorySym; 228 currentClass.sym.members().enter(factorySym); 229 tree.mods.flags |= STATIC; 230 231 /* We may need an epilogue that returns the factory product, but we can't eagerly insert 232 a return here, since we don't know much about control flow here. Gen#genMethod 233 will insert a return of the factory product if control does reach the end and would 234 "fall off the cliff" otherwise. 235 */ 236 237 result = tree; 238 return; 239 } 240 super.visitMethodDef(tree); 241 } finally { 242 currentMethod = previousMethod; 243 } 244 } 245 246 @Override 247 public void visitReturn(JCReturn tree) { 248 if (transformToValueFactory()) { 249 result = make.Return(make.Ident(currentMethod.factoryProduct)); 250 } else { 251 super.visitReturn(tree); 252 } 253 } 254 255 /* Note: 1. Assignop does not call for any translation, since value class instance fields are final and 256 so cannot be AssignedOped. 2. Any redundantly qualified this would have been lowered already. 257 */ 258 @Override 259 public void visitAssign(JCAssign tree) { 260 if (transformToValueFactory()) { 261 Symbol symbol = null; 262 switch(tree.lhs.getTag()) { 263 case IDENT: 264 symbol = ((JCIdent)tree.lhs).sym; 265 break; 266 case SELECT: 267 JCFieldAccess fieldAccess = (JCFieldAccess) tree.lhs; 268 if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) { 269 symbol = fieldAccess.sym; 270 } 271 break; 272 default: 273 break; 274 } 275 if (isInstanceMemberAccess(symbol)) { 276 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct); 277 result = make.Assign(facHandle, make.WithField(make.Select(facHandle, symbol), translate(tree.rhs)).setType(currentClass.type)).setType(currentClass.type); 278 if (requireRVal) { 279 result = make.Select(make.Parens((JCExpression) result).setType(currentClass.type), symbol); 280 } 281 return; 282 } 283 } 284 super.visitAssign(tree); 285 } 286 287 @Override 288 public void visitExec(JCExpressionStatement tree) { 289 if (transformToValueFactory()) { 290 tree.expr = translate(tree.expr, false); 291 result = tree; 292 } else { 293 super.visitExec(tree); 294 } 295 } 296 297 @Override 298 public void visitIdent(JCIdent ident) { 299 if (transformToValueFactory()) { 300 Symbol symbol = ident.sym; 301 if (isInstanceMemberAccess(symbol)) { 302 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct); 303 result = make.Select(facHandle, symbol); 304 return; 305 } else if (symbol.name == names._this) { 306 result = make.Ident(currentMethod.factoryProduct); 307 return; 308 } 309 } 310 super.visitIdent(ident); 311 } 312 313 @Override 314 public void visitSelect(JCFieldAccess fieldAccess) { 315 if (transformToValueFactory()) { // Qualified this would have been lowered already. 316 if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) { 317 Symbol symbol = fieldAccess.sym; 318 if (isInstanceMemberAccess(symbol)) { 319 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct); 320 result = make.Select(facHandle, symbol); 321 return; 322 } 323 } 324 } 325 /* If a static member is being selected via a V.ref as a TYP, rewrite 326 V.ref.member to V.member 327 */ 328 fieldAccess.selected = translate(fieldAccess.selected); 329 if (fieldAccess.name != names._class) { // TODO: this and super ?? 330 Symbol sym = TreeInfo.symbol(fieldAccess); 331 Symbol sitesym = TreeInfo.symbol(fieldAccess.selected); 332 Type selectedType = fieldAccess.selected.type; 333 if (selectedType.isReferenceProjection()) { 334 switch (sym.kind) { 335 case MTH: 336 case VAR: 337 if (sym.isStatic() && sitesym != null && sitesym.kind == TYP) { 338 fieldAccess.selected = make.Type(types.erasure(selectedType.valueProjection())); 339 } 340 break; 341 case TYP: 342 fieldAccess.selected = make.Type(types.erasure(selectedType.valueProjection())); 343 break; 344 } 345 } 346 } 347 result = fieldAccess; 348 } 349 350 // Translate a reference style instance creation attempt on a value class to a static factory call. 351 @Override 352 public void visitNewClass(JCNewClass tree) { 353 if (tree.clazz.type.isValueClass()) { 354 // Enclosing instances or anonymous classes should have been eliminated by now. 355 Assert.check(tree.encl == null && tree.def == null); 356 tree.args = translate(tree.args); 357 Assert.check(tree.def == null); 358 MethodSymbol sFactory = getValueObjectFactory((MethodSymbol) tree.constructor); 359 make.at(tree.pos()); 360 JCExpression declClass = make.Type(tree.constructor.owner.type); 361 JCExpression meth = make.Select(declClass, sFactory); 362 meth.type = types.erasure(meth.type); 363 final JCMethodInvocation apply = make.Apply(tree.typeargs, meth, tree.args); 364 apply.varargsElement = tree.varargsElement; 365 apply.type = meth.type.getReturnType(); 366 result = apply; 367 return; 368 } 369 super.visitNewClass(tree); 370 } 371 372 // Utility methods ... 373 private boolean transformToValueFactory() { 374 // We lower any constructors in abstract value classes to <init> methods while a 375 // constructor in a concrete value class is lowered into a static value factory method 376 return currentClass != null && (currentClass.sym.flags() & (ABSTRACT | VALUE_CLASS)) == VALUE_CLASS && currentMethod != null && currentMethod.sym.name == names.vnew; 377 } 378 379 private boolean isInstanceMemberAccess(Symbol symbol) { 380 return symbol != null 381 && (symbol.name != names._this && symbol.name != names._super) 382 && (symbol.kind == VAR || symbol.kind == MTH) 383 && symbol.owner == currentClass.sym && !symbol.isStatic(); 384 } 385 386 private MethodSymbol getValueObjectFactory(MethodSymbol init) { 387 Assert.check(init.name.equals(names.vnew)); 388 Assert.check(init.owner.type.isValueClass()); 389 MethodSymbol factory = init2factory.get(init); 390 if (factory != null) 391 return factory; 392 393 MethodType factoryType = new MethodType(init.type.getParameterTypes(), 394 init.owner.type, 395 init.type.getThrownTypes(), 396 init.owner.type.tsym); 397 factory = new MethodSymbol(init.flags_field | STATIC, 398 names.vnew, 399 factoryType, 400 init.owner); 401 factory.params = init.params; 402 // Re-patch the return type on the erased method type, or code generation will fail 403 factory.erasure_field = new MethodType(init.erasure(types).getParameterTypes(), 404 init.owner.type, 405 init.type.getThrownTypes(), 406 init.owner.type.tsym); 407 factory.setAttributes(init); 408 init2factory.put(init, factory); 409 return factory; 410 } 411 412 /** Return the *statement* in the constructor that `chains' to another constructor call either 413 * in the same class or its superclass. One MUST exist except for jlO, though may be buried 414 * under synthetic initializations. 415 */ 416 private JCExpressionStatement chainedConstructorCall(JCMethodDecl md) { 417 if (names.isInitOrVNew(md.name) && md.body != null) { 418 for (JCStatement statement : md.body.stats) { 419 if (statement.hasTag(EXEC)) { 420 JCExpressionStatement exec = (JCExpressionStatement)statement; 421 if (exec.expr.hasTag(APPLY)) { 422 JCMethodInvocation apply = (JCMethodInvocation)exec.expr; 423 Name name = TreeInfo.name(apply.meth); 424 if (name == names._super || name == names._this) 425 return exec; 426 } 427 } 428 } 429 } 430 return null; 431 } 432 }