1 /*
   2  * Copyright (c) 2018, 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.Scope.WriteableScope;
  31 import com.sun.tools.javac.code.Symbol;
  32 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  33 import com.sun.tools.javac.code.Symbol.VarSymbol;
  34 import com.sun.tools.javac.code.Symtab;
  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 import com.sun.tools.javac.util.Options;
  58 
  59 import java.util.HashMap;
  60 import java.util.Map;
  61 
  62 import static com.sun.tools.javac.code.Flags.STATIC;
  63 import static com.sun.tools.javac.code.Kinds.Kind.MTH;
  64 import static com.sun.tools.javac.code.Kinds.Kind.VAR;
  65 import static com.sun.tools.javac.tree.JCTree.Tag.APPLY;
  66 import static com.sun.tools.javac.tree.JCTree.Tag.EXEC;
  67 import static com.sun.tools.javac.tree.JCTree.Tag.IDENT;
  68 
  69 /**
  70  * This pass translates value constructors into static factory methods and patches up constructor
  71  * calls to become invocations of those static factory methods.
  72  *
  73  * We get commissioned as a subpass of Gen. Constructor trees undergo plenty of change in Lower
  74  * (enclosing instance injection, captured locals ...) and in Gen (instance field initialization,
  75  * see normalizeDefs) and so it is most effective to wait until things reach a quiescent state
  76  * before undertaking the tinkering that we do.
  77  *
  78  * See https://bugs.openjdk.java.net/browse/JDK-8198749 for the kind of transformations we do.
  79  *
  80  */
  81 public class TransValues extends TreeTranslator {
  82 
  83     protected static final Context.Key<TransValues> transValuesKey = new Context.Key<>();
  84 
  85     private Symtab syms;
  86     private TreeMaker make;
  87     private Types types;
  88     private Names names;
  89 
  90     /* Is an assignment undergoing translation just an assignment statement ?
  91        Or is also a value ??
  92     */
  93     private boolean requireRVal;
  94 
  95     // class currently undergoing translation.
  96     private JCClassDecl currentClass;
  97 
  98     // method currently undergoing translation.
  99     private JCMethodDecl currentMethod;
 100 
 101     // list of factories synthesized so far.
 102     private List<JCTree> staticFactories;
 103 
 104     private boolean staticInitValueFactory;
 105 
 106     // Map from constructor symbols to factory symbols.
 107     private Map<MethodSymbol, MethodSymbol> init2factory = new HashMap<>();
 108 
 109     public static TransValues instance(Context context) {
 110         TransValues instance = context.get(transValuesKey);
 111         if (instance == null)
 112             instance = new TransValues(context);
 113         return instance;
 114     }
 115 
 116     protected TransValues(Context context) {
 117         context.put(transValuesKey, this);
 118         syms = Symtab.instance(context);
 119         make = TreeMaker.instance(context);
 120         types = Types.instance(context);
 121         names = Names.instance(context);
 122         Options options = Options.instance(context);
 123         staticInitValueFactory = options.isSet("staticInitValueFactory");
 124         staticInitValueFactory |= !options.isSet("noStaticInitValueFactory");
 125     }
 126 
 127     @SuppressWarnings("unchecked")
 128     public <T extends JCTree> T translate(T tree, boolean requireRVal) {
 129         boolean priorRequireRVal = this.requireRVal;
 130         try {
 131             this.requireRVal = requireRVal;
 132             if (tree == null) {
 133                 return null;
 134             } else {
 135                 tree.accept(this);
 136                 JCTree tmpResult = this.result;
 137                 this.result = null;
 138                 return (T)tmpResult; // XXX cast
 139             }
 140         } finally {
 141              this.requireRVal = priorRequireRVal;
 142         }
 143     }
 144 
 145     @Override
 146     public <T extends JCTree> T translate(T tree) {
 147         return translate(tree, true);
 148     }
 149 
 150     public JCClassDecl translateTopLevelClass(JCClassDecl classDecl, TreeMaker make) {
 151         try {
 152             this.make = make;
 153             translate(classDecl);
 154         } finally {
 155             // note that recursive invocations of this method fail hard
 156             this.make = null;
 157         }
 158         init2factory = new HashMap<>();
 159         return classDecl;
 160     }
 161 
 162     @Override
 163     public void visitClassDef(JCClassDecl classDecl) {
 164         JCClassDecl previousClass = currentClass;
 165         List<JCTree> previousFactories = staticFactories;
 166         staticFactories = List.nil();
 167         currentClass = classDecl;
 168         try {
 169             super.visitClassDef(classDecl);
 170             classDecl.defs = classDecl.defs.appendList(staticFactories);
 171             staticFactories = List.nil();
 172         }
 173         finally {
 174             currentClass = previousClass;
 175             staticFactories = previousFactories;
 176         }
 177     }
 178 
 179     @Override
 180     public void visitMethodDef(JCMethodDecl tree) {
 181         JCMethodDecl previousMethod = currentMethod;
 182         currentMethod = tree;
 183         try {
 184             if (constructingValue()) {
 185 
 186                 if (staticInitValueFactory) {
 187                     visitMethodDef2(tree);
 188                     return;
 189                 }
 190 
 191                 /* Mutate this value constructor into an equivalent static value factory, leaving in place
 192                    a dummy constructor. (A placeholder constructor is still required so that Attr and
 193                    other earlier pipeline stages will see the required constructors to bind any reference
 194                    style instantiations to.)
 195 
 196                    The verifier ensures that the `new' bytecode can never be used with a value class, so
 197                    constructor body can be essentially wiped out except for a call that chains to super
 198                    constructor. (The latter is mandated by the verifier.)
 199                 */
 200 
 201                 make.at(tree.pos());
 202                 JCExpressionStatement exec = chainedConstructorCall(tree);
 203                 Assert.check(exec != null && TreeInfo.isSelfCall(exec));
 204                 JCMethodInvocation call = (JCMethodInvocation) exec.expr;
 205 
 206                 /* Unlike the reference construction sequence where `this' is allocated ahead of time and
 207                    is passed as an argument into the <init> method, a value factory must allocate the value
 208                    instance that forms the `product' by itself. We do that by injecting a prologue here.
 209                 */
 210                 VarSymbol product = currentMethod.factoryProduct = new VarSymbol(0, names.dollarValue, currentClass.sym.type, currentMethod.sym); // TODO: owner needs rewiring
 211                 JCExpression rhs;
 212 
 213                 final Name name = TreeInfo.name(call.meth);
 214                 MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth);
 215                 if (names._super.equals(name)) { // "initial" constructor.
 216                     // Synthesize code to allocate factory "product" via: V $this = V.default;
 217                     Assert.check(symbol.owner == syms.objectType.tsym);
 218                     Assert.check(symbol.type.getParameterTypes().size() == 0);
 219                     final JCExpression type = make.Type(currentClass.type);
 220                     rhs = make.Select(type, new VarSymbol(STATIC, names._default, currentClass.type, currentClass.sym));
 221                 } else {
 222                     // This must be a chained call of form `this(args)'; Mutate it into a factory invocation i.e V $this = V.$makeValue$(args);
 223                     Assert.check(TreeInfo.name(TreeInfo.firstConstructorCall(tree).meth) == names._this);
 224                     MethodSymbol factory = getValueFactory(symbol);
 225                     final JCIdent ident = make.Ident(factory);
 226                     rhs = make.App(ident, call.args);
 227                     ((JCMethodInvocation)rhs).varargsElement = call.varargsElement;
 228                 }
 229 
 230                 /* The value product allocation prologue must precede any synthetic inits !!!
 231                    as these may reference `this' which gets pre-allocated for references but
 232                    not for values.
 233                 */
 234                 JCStatement prologue = make.VarDef(product, rhs);
 235                 tree.body.stats = tree.body.stats.prepend(prologue).diff(List.of(exec));
 236                 tree.body = translate(tree.body);
 237 
 238                 /* We may need an epilogue that returns the value product, but we can't eagerly insert
 239                    a return here, since we don't know much about control flow here. Gen#genMethod
 240                    will insert a return of the factory product if control does reach the end and would
 241                    "fall off the cliff" otherwise.
 242                 */
 243 
 244                 /* Create a factory method declaration and pass on ownership of the translated ctor body
 245                    to it, wiping out the ctor body itself.
 246                 */
 247                 MethodSymbol factorySym = getValueFactory(tree.sym);
 248                 JCMethodDecl factoryMethod = make.MethodDef(make.Modifiers(tree.mods.flags | Flags.SYNTHETIC | STATIC, tree.mods.annotations),
 249                         factorySym.name,
 250                         make.Type(factorySym.type.getReturnType()),
 251                         tree.typarams,
 252                         null,
 253                         tree.params,
 254                         tree.thrown,
 255                         tree.body,
 256                         null);
 257                 factoryMethod.sym = factorySym;
 258                 factoryMethod.setType(factorySym.type);
 259                 factoryMethod.factoryProduct = product;
 260                 staticFactories = staticFactories.append(factoryMethod);
 261                 currentClass.sym.members().enter(factorySym);
 262 
 263                 // wipe out the body of the ctor and insert just a super call.
 264                 MethodSymbol jlOCtor = getDefaultConstructor(syms.objectType.tsym);
 265                 JCExpression meth = make.Ident(names._super).setType(jlOCtor.type);
 266                 TreeInfo.setSymbol(meth, jlOCtor);
 267 
 268                 final JCExpressionStatement superCall = make.Exec(make.Apply(null, meth, List.nil()).setType(syms.voidType));
 269                 tree.body = make.at(tree.body).Block(0, List.of(superCall));
 270                 result = tree;
 271                 return;
 272             }
 273             super.visitMethodDef(tree);
 274         } finally {
 275             currentMethod = previousMethod;
 276         }
 277     }
 278 
 279     public void visitMethodDef2(JCMethodDecl tree) {
 280         JCMethodDecl previousMethod = currentMethod;
 281         currentMethod = tree;
 282         try {
 283             if (constructingValue()) {
 284 
 285                 /* Mutate this value constructor into an equivalent static value factory, leaving in place
 286                    a dummy constructor. (A placeholder constructor is still required so that Attr and
 287                    other earlier pipeline stages will see the required constructors to bind any reference
 288                    style instantiations to.)
 289 
 290                    The verifier ensures that the `new' bytecode can never be used with a value class, so
 291                    constructor body can be essentially wiped out except for a call that chains to super
 292                    constructor. (The latter is mandated by the verifier.)
 293                 */
 294 
 295                 make.at(tree.pos());
 296                 JCExpressionStatement exec = chainedConstructorCall(tree);
 297                 Assert.check(exec != null && TreeInfo.isSelfCall(exec));
 298                 JCMethodInvocation call = (JCMethodInvocation) exec.expr;
 299 
 300                 /* Unlike the reference construction sequence where `this' is allocated ahead of time and
 301                    is passed as an argument into the <init> method, a value factory must allocate the value
 302                    instance that forms the `product' by itself. We do that by injecting a prologue here.
 303                 */
 304                 VarSymbol product = currentMethod.factoryProduct = new VarSymbol(0, names.dollarValue, currentClass.sym.type, currentMethod.sym); // TODO: owner needs rewiring
 305                 JCExpression rhs;
 306 
 307                 final Name name = TreeInfo.name(call.meth);
 308                 MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth);
 309                 if (names._super.equals(name)) { // "initial" constructor.
 310                     // Synthesize code to allocate factory "product" via: V $this = V.default;
 311                     Assert.check(symbol.owner == syms.objectType.tsym);
 312                     Assert.check(symbol.type.getParameterTypes().size() == 0);
 313                     final JCExpression type = make.Type(currentClass.type);
 314                     rhs = make.Select(type, new VarSymbol(STATIC, names._default, currentClass.type, currentClass.sym));
 315                 } else {
 316                     // This must be a chained call of form `this(args)'; Mutate it into a factory invocation i.e V $this = V.$makeValue$(args);
 317                     Assert.check(TreeInfo.name(TreeInfo.firstConstructorCall(tree).meth) == names._this);
 318                     MethodSymbol factory = getValueFactory(symbol);
 319                     final JCIdent ident = make.Ident(factory);
 320                     rhs = make.App(ident, call.args);
 321                     ((JCMethodInvocation)rhs).varargsElement = call.varargsElement;
 322                 }
 323 
 324                 /* The value product allocation prologue must precede any synthetic inits !!!
 325                    as these may reference `this' which gets pre-allocated for references but
 326                    not for values.
 327                 */
 328                 JCStatement prologue = make.VarDef(product, rhs);
 329                 tree.body.stats = tree.body.stats.prepend(prologue).diff(List.of(exec));
 330                 tree.body = translate(tree.body);
 331 
 332                 MethodSymbol factorySym = getValueFactory(tree.sym);
 333                 currentMethod.setType(factorySym.type);
 334                 currentMethod.factoryProduct = product;
 335                 currentClass.sym.members().remove(tree.sym);
 336                 tree.sym = factorySym;
 337                 currentClass.sym.members().enter(factorySym);
 338                 tree.mods.flags |= STATIC;
 339 
 340                 /* We may need an epilogue that returns the value product, but we can't eagerly insert
 341                    a return here, since we don't know much about control flow here. Gen#genMethod
 342                    will insert a return of the factory product if control does reach the end and would
 343                    "fall off the cliff" otherwise.
 344                 */
 345 
 346                 result = tree;
 347                 return;
 348             }
 349             super.visitMethodDef(tree);
 350         } finally {
 351             currentMethod = previousMethod;
 352         }
 353     }
 354 
 355     @Override
 356     public void visitReturn(JCReturn tree) {
 357         if (constructingValue()) {
 358             result = make.Return(make.Ident(currentMethod.factoryProduct));
 359         } else {
 360             super.visitReturn(tree);
 361         }
 362     }
 363 
 364     /* Note: 1. Assignop does not call for any translation, since value instance fields are final and
 365        so cannot be AssignedOped. 2. Any redundantly qualified this would have been lowered already.
 366     */
 367     @Override
 368     public void visitAssign(JCAssign tree) {
 369         if (constructingValue()) {
 370             Symbol symbol = null;
 371             switch(tree.lhs.getTag()) {
 372                 case IDENT:
 373                     symbol = ((JCIdent)tree.lhs).sym;
 374                     break;
 375                 case SELECT:
 376                     JCFieldAccess fieldAccess = (JCFieldAccess) tree.lhs;
 377                     if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) {
 378                         symbol = fieldAccess.sym;
 379                     }
 380                     break;
 381                 default:
 382                     break;
 383             }
 384             if (isInstanceAccess(symbol)) {
 385                 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 386                 result = make.Assign(facHandle, make.WithField(make.Select(facHandle, symbol), translate(tree.rhs)).setType(currentClass.type)).setType(currentClass.type);
 387                 if (requireRVal) {
 388                     result = make.Select(make.Parens((JCExpression) result).setType(currentClass.type), symbol);
 389                 }
 390                 return;
 391             }
 392         }
 393         super.visitAssign(tree);
 394     }
 395 
 396     @Override
 397     public void visitExec(JCExpressionStatement tree) {
 398         if (constructingValue()) {
 399             tree.expr = translate(tree.expr, false);
 400             result = tree;
 401         } else {
 402             super.visitExec(tree);
 403         }
 404     }
 405 
 406     @Override
 407     public void visitIdent(JCIdent ident) {
 408         if (constructingValue()) {
 409             Symbol symbol = ident.sym;
 410             if (isInstanceAccess(symbol)) {
 411                 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 412                 result = make.Select(facHandle, symbol);
 413                 return;
 414             }
 415         }
 416         super.visitIdent(ident);
 417     }
 418 
 419     @Override
 420     public void visitSelect(JCFieldAccess fieldAccess) {
 421         if (constructingValue()) { // Qualified this would have been lowered already.
 422             if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) {
 423                 Symbol symbol = fieldAccess.sym;
 424                 if (isInstanceAccess(symbol)) {
 425                     final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 426                     result = make.Select(facHandle, symbol);
 427                     return;
 428                 }
 429             }
 430         }
 431         super.visitSelect(fieldAccess);
 432     }
 433 
 434     // Translate a reference style instance creation attempt on a value type to a static factory call.
 435     @Override
 436     public void visitNewClass(JCNewClass tree) {
 437         if (types.isValue(tree.clazz.type)) {
 438             tree.encl = translate(tree.encl);
 439             tree.args = translate(tree.args);
 440             Assert.check(tree.def == null);
 441             MethodSymbol sFactory = getValueFactory((MethodSymbol) tree.constructor);
 442             make.at(tree.pos());
 443             JCExpression meth = tree.encl == null ? make.Ident(sFactory): make.Select(tree.encl, sFactory); // TODO: tree.encl must have been folded already into a synth argument and nullified
 444             meth.type = types.erasure(meth.type);
 445             final JCMethodInvocation apply = make.Apply(tree.typeargs, meth, tree.args);
 446             apply.varargsElement = tree.varargsElement;
 447             apply.type = meth.type.getReturnType();
 448             result = apply;
 449             return;
 450         }
 451         super.visitNewClass(tree);
 452     }
 453 
 454     // Utility methods ...
 455     private boolean constructingValue() {
 456         return currentClass != null && (currentClass.sym.flags() & Flags.VALUE) != 0 && currentMethod != null && currentMethod.sym.isConstructor();
 457     }
 458 
 459     private boolean isInstanceAccess(Symbol symbol) {
 460         return symbol != null && (symbol.kind == VAR || symbol.kind == MTH) && symbol.owner == currentClass.sym && !symbol.isStatic();
 461     }
 462 
 463     private MethodSymbol getValueFactory(MethodSymbol init) {
 464         Assert.check(init.name.equals(names.init));
 465         Assert.check(types.isValue(init.owner.type));
 466         MethodSymbol factory = init2factory.get(init);
 467         if (factory != null)
 468             return factory;
 469 
 470         final WriteableScope classScope = init.owner.members();
 471         Assert.check(classScope.includes(init, LookupKind.NON_RECURSIVE));
 472 
 473         MethodType factoryType = new MethodType(init.externalType(types).getParameterTypes(), // init.externalType to account for synthetics.
 474                                                 init.owner.type,
 475                                                 init.type.getThrownTypes(),
 476                                                 init.owner.type.tsym);
 477         factory = new MethodSymbol(init.flags_field | STATIC | (staticInitValueFactory ? 0 : Flags.SYNTHETIC),
 478                                         staticInitValueFactory ? names.init : names.makeValue,
 479                                         factoryType,
 480                                         init.owner);
 481         factory.setAttributes(init);
 482         init2factory.put(init, factory);
 483         return factory;
 484     }
 485 
 486     /** Return the *statement* in the constructor that `chains' to another constructor call either
 487      *  in the same class or its superclass. One MUST exist except for jlO, though may be buried
 488      *  under synthetic initializations.
 489      */
 490     private JCExpressionStatement chainedConstructorCall(JCMethodDecl md) {
 491         if (md.name == names.init && md.body != null) {
 492             for (JCStatement statement : md.body.stats) {
 493                 if (statement.hasTag(EXEC)) {
 494                     JCExpressionStatement exec = (JCExpressionStatement)statement;
 495                     if (exec.expr.hasTag(APPLY)) {
 496                         JCMethodInvocation apply = (JCMethodInvocation)exec.expr;
 497                         Name name = TreeInfo.name(apply.meth);
 498                         if (name == names._super || name == names._this)
 499                             return exec;
 500                     }
 501                 }
 502             }
 503         }
 504         return null;
 505     }
 506 
 507     private MethodSymbol getDefaultConstructor(Symbol klass) {
 508         for (Symbol method : klass.members().getSymbolsByName(names.init, s->s.kind == MTH && s.type.getParameterTypes().size() == 0, LookupKind.NON_RECURSIVE)) {
 509             return (MethodSymbol) method;
 510         }
 511         // class defines a non-nullary but no nullary constructor, fabricate a symbol.
 512         MethodType dctorType = new MethodType(List.nil(),
 513                 klass.type,
 514                 List.nil(),
 515                 klass.type.tsym);
 516         return new MethodSymbol(Flags.PUBLIC,
 517                 names.init,
 518                 dctorType,
 519                 klass);
 520     }
 521 }