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