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     }
 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 (constructingValue()) {
 184 
 185                 if (staticInitValueFactory) {
 186                     visitMethodDef2(tree);
 187                     return;
 188                 }
 189 
 190                 /* Mutate this value constructor into an equivalent static value factory, leaving in place
 191                    a dummy constructor. (A placeholder constructor is still required so that Attr and
 192                    other earlier pipeline stages will see the required constructors to bind any reference
 193                    style instantiations to.)
 194 
 195                    The verifier ensures that the `new' bytecode can never be used with a value class, so
 196                    constructor body can be essentially wiped out except for a call that chains to super
 197                    constructor. (The latter is mandated by the verifier.)
 198                 */
 199 
 200                 make.at(tree.pos());
 201                 JCExpressionStatement exec = chainedConstructorCall(tree);
 202                 Assert.check(exec != null && TreeInfo.isSelfCall(exec));
 203                 JCMethodInvocation call = (JCMethodInvocation) exec.expr;
 204 
 205                 /* Unlike the reference construction sequence where `this' is allocated ahead of time and
 206                    is passed as an argument into the <init> method, a value factory must allocate the value
 207                    instance that forms the `product' by itself. We do that by injecting a prologue here.
 208                 */
 209                 VarSymbol product = currentMethod.factoryProduct = new VarSymbol(0, names.dollarValue, currentClass.sym.type, currentMethod.sym); // TODO: owner needs rewiring
 210                 JCExpression rhs;
 211 
 212                 final Name name = TreeInfo.name(call.meth);
 213                 MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth);
 214                 if (names._super.equals(name)) { // "initial" constructor.
 215                     // Synthesize code to allocate factory "product" via: V $this = V.default;
 216                     Assert.check(symbol.owner == syms.objectType.tsym);
 217                     Assert.check(symbol.type.getParameterTypes().size() == 0);
 218                     final JCExpression type = make.Type(currentClass.type);
 219                     rhs = make.Select(type, new VarSymbol(STATIC, names._default, currentClass.type, currentClass.sym));
 220                 } else {
 221                     // This must be a chained call of form `this(args)'; Mutate it into a factory invocation i.e V $this = V.$makeValue$(args);
 222                     Assert.check(TreeInfo.name(TreeInfo.firstConstructorCall(tree).meth) == names._this);
 223                     MethodSymbol factory = getValueFactory(symbol);
 224                     final JCIdent ident = make.Ident(factory);
 225                     rhs = make.App(ident, call.args);
 226                     ((JCMethodInvocation)rhs).varargsElement = call.varargsElement;
 227                 }
 228 
 229                 /* The value product allocation prologue must precede any synthetic inits !!!
 230                    as these may reference `this' which gets pre-allocated for references but
 231                    not for values.
 232                 */
 233                 JCStatement prologue = make.VarDef(product, rhs);
 234                 tree.body.stats = tree.body.stats.prepend(prologue).diff(List.of(exec));
 235                 tree.body = translate(tree.body);
 236 
 237                 /* We may need an epilogue that returns the value product, but we can't eagerly insert
 238                    a return here, since we don't know much about control flow here. Gen#genMethod
 239                    will insert a return of the factory product if control does reach the end and would
 240                    "fall off the cliff" otherwise.
 241                 */
 242 
 243                 /* Create a factory method declaration and pass on ownership of the translated ctor body
 244                    to it, wiping out the ctor body itself.
 245                 */
 246                 MethodSymbol factorySym = getValueFactory(tree.sym);
 247                 JCMethodDecl factoryMethod = make.MethodDef(make.Modifiers(tree.mods.flags | Flags.SYNTHETIC | STATIC, tree.mods.annotations),
 248                         factorySym.name,
 249                         make.Type(factorySym.type.getReturnType()),
 250                         tree.typarams,
 251                         null,
 252                         tree.params,
 253                         tree.thrown,
 254                         tree.body,
 255                         null);
 256                 factoryMethod.sym = factorySym;
 257                 factoryMethod.setType(factorySym.type);
 258                 factoryMethod.factoryProduct = product;
 259                 staticFactories = staticFactories.append(factoryMethod);
 260                 currentClass.sym.members().enter(factorySym);
 261 
 262                 // wipe out the body of the ctor and insert just a super call.
 263                 MethodSymbol jlOCtor = getDefaultConstructor(syms.objectType.tsym);
 264                 JCExpression meth = make.Ident(names._super).setType(jlOCtor.type);
 265                 TreeInfo.setSymbol(meth, jlOCtor);
 266 
 267                 final JCExpressionStatement superCall = make.Exec(make.Apply(null, meth, List.nil()).setType(syms.voidType));
 268                 tree.body = make.at(tree.body).Block(0, List.of(superCall));
 269                 result = tree;
 270                 return;
 271             }
 272             super.visitMethodDef(tree);
 273         } finally {
 274             currentMethod = previousMethod;
 275         }
 276     }
 277 
 278     public void visitMethodDef2(JCMethodDecl tree) {
 279         JCMethodDecl previousMethod = currentMethod;
 280         currentMethod = tree;
 281         try {
 282             if (constructingValue()) {
 283 
 284                 /* Mutate this value constructor into an equivalent static value factory, leaving in place
 285                    a dummy constructor. (A placeholder constructor is still required so that Attr and
 286                    other earlier pipeline stages will see the required constructors to bind any reference
 287                    style instantiations to.)
 288 
 289                    The verifier ensures that the `new' bytecode can never be used with a value class, so
 290                    constructor body can be essentially wiped out except for a call that chains to super
 291                    constructor. (The latter is mandated by the verifier.)
 292                 */
 293 
 294                 make.at(tree.pos());
 295                 JCExpressionStatement exec = chainedConstructorCall(tree);
 296                 Assert.check(exec != null && TreeInfo.isSelfCall(exec));
 297                 JCMethodInvocation call = (JCMethodInvocation) exec.expr;
 298 
 299                 /* Unlike the reference construction sequence where `this' is allocated ahead of time and
 300                    is passed as an argument into the <init> method, a value factory must allocate the value
 301                    instance that forms the `product' by itself. We do that by injecting a prologue here.
 302                 */
 303                 VarSymbol product = currentMethod.factoryProduct = new VarSymbol(0, names.dollarValue, currentClass.sym.type, currentMethod.sym); // TODO: owner needs rewiring
 304                 JCExpression rhs;
 305 
 306                 final Name name = TreeInfo.name(call.meth);
 307                 MethodSymbol symbol = (MethodSymbol)TreeInfo.symbol(call.meth);
 308                 if (names._super.equals(name)) { // "initial" constructor.
 309                     // Synthesize code to allocate factory "product" via: V $this = V.default;
 310                     Assert.check(symbol.owner == syms.objectType.tsym);
 311                     Assert.check(symbol.type.getParameterTypes().size() == 0);
 312                     final JCExpression type = make.Type(currentClass.type);
 313                     rhs = make.Select(type, new VarSymbol(STATIC, names._default, currentClass.type, currentClass.sym));
 314                 } else {
 315                     // This must be a chained call of form `this(args)'; Mutate it into a factory invocation i.e V $this = V.$makeValue$(args);
 316                     Assert.check(TreeInfo.name(TreeInfo.firstConstructorCall(tree).meth) == names._this);
 317                     MethodSymbol factory = getValueFactory(symbol);
 318                     final JCIdent ident = make.Ident(factory);
 319                     rhs = make.App(ident, call.args);
 320                     ((JCMethodInvocation)rhs).varargsElement = call.varargsElement;
 321                 }
 322 
 323                 /* The value product allocation prologue must precede any synthetic inits !!!
 324                    as these may reference `this' which gets pre-allocated for references but
 325                    not for values.
 326                 */
 327                 JCStatement prologue = make.VarDef(product, rhs);
 328                 tree.body.stats = tree.body.stats.prepend(prologue).diff(List.of(exec));
 329                 tree.body = translate(tree.body);
 330 
 331                 MethodSymbol factorySym = getValueFactory(tree.sym);
 332                 currentMethod.setType(factorySym.type);
 333                 currentMethod.factoryProduct = product;
 334                 currentClass.sym.members().remove(tree.sym);
 335                 tree.sym = factorySym;
 336                 currentClass.sym.members().enter(factorySym);
 337                 tree.mods.flags |= STATIC;
 338 
 339                 /* We may need an epilogue that returns the value product, but we can't eagerly insert
 340                    a return here, since we don't know much about control flow here. Gen#genMethod
 341                    will insert a return of the factory product if control does reach the end and would
 342                    "fall off the cliff" otherwise.
 343                 */
 344 
 345                 result = tree;
 346                 return;
 347             }
 348             super.visitMethodDef(tree);
 349         } finally {
 350             currentMethod = previousMethod;
 351         }
 352     }
 353 
 354     @Override
 355     public void visitReturn(JCReturn tree) {
 356         if (constructingValue()) {
 357             result = make.Return(make.Ident(currentMethod.factoryProduct));
 358         } else {
 359             super.visitReturn(tree);
 360         }
 361     }
 362 
 363     /* Note: 1. Assignop does not call for any translation, since value instance fields are final and
 364        so cannot be AssignedOped. 2. Any redundantly qualified this would have been lowered already.
 365     */
 366     @Override
 367     public void visitAssign(JCAssign tree) {
 368         if (constructingValue()) {
 369             Symbol symbol = null;
 370             switch(tree.lhs.getTag()) {
 371                 case IDENT:
 372                     symbol = ((JCIdent)tree.lhs).sym;
 373                     break;
 374                 case SELECT:
 375                     JCFieldAccess fieldAccess = (JCFieldAccess) tree.lhs;
 376                     if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) {
 377                         symbol = fieldAccess.sym;
 378                     }
 379                     break;
 380                 default:
 381                     break;
 382             }
 383             if (isInstanceAccess(symbol)) {
 384                 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 385                 result = make.Assign(facHandle, make.WithField(make.Select(facHandle, symbol), translate(tree.rhs)).setType(currentClass.type)).setType(currentClass.type);
 386                 if (requireRVal) {
 387                     result = make.Select(make.Parens((JCExpression) result).setType(currentClass.type), symbol);
 388                 }
 389                 return;
 390             }
 391         }
 392         super.visitAssign(tree);
 393     }
 394 
 395     @Override
 396     public void visitExec(JCExpressionStatement tree) {
 397         if (constructingValue()) {
 398             tree.expr = translate(tree.expr, false);
 399             result = tree;
 400         } else {
 401             super.visitExec(tree);
 402         }
 403     }
 404 
 405     @Override
 406     public void visitIdent(JCIdent ident) {
 407         if (constructingValue()) {
 408             Symbol symbol = ident.sym;
 409             if (isInstanceAccess(symbol)) {
 410                 final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 411                 result = make.Select(facHandle, symbol);
 412                 return;
 413             }
 414         }
 415         super.visitIdent(ident);
 416     }
 417 
 418     @Override
 419     public void visitSelect(JCFieldAccess fieldAccess) {
 420         if (constructingValue()) { // Qualified this would have been lowered already.
 421             if (fieldAccess.selected.hasTag(IDENT) && ((JCIdent)fieldAccess.selected).name == names._this) {
 422                 Symbol symbol = fieldAccess.sym;
 423                 if (isInstanceAccess(symbol)) {
 424                     final JCIdent facHandle = make.Ident(currentMethod.factoryProduct);
 425                     result = make.Select(facHandle, symbol);
 426                     return;
 427                 }
 428             }
 429         }
 430         super.visitSelect(fieldAccess);
 431     }
 432 
 433     // Translate a reference style instance creation attempt on a value type to a static factory call.
 434     @Override
 435     public void visitNewClass(JCNewClass tree) {
 436         if (types.isValue(tree.clazz.type)) {
 437             tree.encl = translate(tree.encl);
 438             tree.args = translate(tree.args);
 439             Assert.check(tree.def == null);
 440             MethodSymbol sFactory = getValueFactory((MethodSymbol) tree.constructor);
 441             make.at(tree.pos());
 442             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
 443             meth.type = types.erasure(meth.type);
 444             final JCMethodInvocation apply = make.Apply(tree.typeargs, meth, tree.args);
 445             apply.varargsElement = tree.varargsElement;
 446             apply.type = meth.type.getReturnType();
 447             result = apply;
 448             return;
 449         }
 450         super.visitNewClass(tree);
 451     }
 452 
 453     // Utility methods ...
 454     private boolean constructingValue() {
 455         return currentClass != null && (currentClass.sym.flags() & Flags.VALUE) != 0 && currentMethod != null && currentMethod.sym.isConstructor();
 456     }
 457 
 458     private boolean isInstanceAccess(Symbol symbol) {
 459         return symbol != null && (symbol.kind == VAR || symbol.kind == MTH) && symbol.owner == currentClass.sym && !symbol.isStatic();
 460     }
 461 
 462     private MethodSymbol getValueFactory(MethodSymbol init) {
 463         Assert.check(init.name.equals(names.init));
 464         Assert.check(types.isValue(init.owner.type));
 465         MethodSymbol factory = init2factory.get(init);
 466         if (factory != null)
 467             return factory;
 468 
 469         final WriteableScope classScope = init.owner.members();
 470         Assert.check(classScope.includes(init, LookupKind.NON_RECURSIVE));
 471 
 472         MethodType factoryType = new MethodType(init.externalType(types).getParameterTypes(), // init.externalType to account for synthetics.
 473                                                 init.owner.type,
 474                                                 init.type.getThrownTypes(),
 475                                                 init.owner.type.tsym);
 476         factory = new MethodSymbol(init.flags_field | STATIC | (staticInitValueFactory ? 0 : Flags.SYNTHETIC),
 477                                         staticInitValueFactory ? names.init : names.makeValue,
 478                                         factoryType,
 479                                         init.owner);
 480         factory.setAttributes(init);
 481         init2factory.put(init, factory);
 482         return factory;
 483     }
 484 
 485     /** Return the *statement* in the constructor that `chains' to another constructor call either
 486      *  in the same class or its superclass. One MUST exist except for jlO, though may be buried
 487      *  under synthetic initializations.
 488      */
 489     private JCExpressionStatement chainedConstructorCall(JCMethodDecl md) {
 490         if (md.name == names.init && md.body != null) {
 491             for (JCStatement statement : md.body.stats) {
 492                 if (statement.hasTag(EXEC)) {
 493                     JCExpressionStatement exec = (JCExpressionStatement)statement;
 494                     if (exec.expr.hasTag(APPLY)) {
 495                         JCMethodInvocation apply = (JCMethodInvocation)exec.expr;
 496                         Name name = TreeInfo.name(apply.meth);
 497                         if (name == names._super || name == names._this)
 498                             return exec;
 499                     }
 500                 }
 501             }
 502         }
 503         return null;
 504     }
 505 
 506     private MethodSymbol getDefaultConstructor(Symbol klass) {
 507         for (Symbol method : klass.members().getSymbolsByName(names.init, s->s.kind == MTH && s.type.getParameterTypes().size() == 0, LookupKind.NON_RECURSIVE)) {
 508             return (MethodSymbol) method;
 509         }
 510         // class defines a non-nullary but no nullary constructor, fabricate a symbol.
 511         MethodType dctorType = new MethodType(List.nil(),
 512                 klass.type,
 513                 List.nil(),
 514                 klass.type.tsym);
 515         return new MethodSymbol(Flags.PUBLIC,
 516                 names.init,
 517                 dctorType,
 518                 klass);
 519     }
 520 }