1 /*
   2  * Copyright (c) 2017, 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 java.util.EnumSet;
  29 import java.util.HashMap;
  30 import java.util.Map;
  31 import java.util.Optional;
  32 import java.util.Set;
  33 
  34 import com.sun.tools.javac.code.Source;
  35 import com.sun.tools.javac.code.Source.Feature;
  36 import com.sun.tools.javac.code.Symbol;
  37 import com.sun.tools.javac.code.Symbol.DynamicVarSymbol;
  38 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
  39 import com.sun.tools.javac.code.Symbol.IntrinsicsLDCMethodSymbol;
  40 import com.sun.tools.javac.code.Symbol.MethodSymbol;
  41 import com.sun.tools.javac.code.Symbol.VarSymbol;
  42 import com.sun.tools.javac.code.Symtab;
  43 import com.sun.tools.javac.code.Type;
  44 import com.sun.tools.javac.code.Type.MethodType;
  45 import com.sun.tools.javac.code.Types;
  46 import com.sun.tools.javac.jvm.Pool;
  47 import com.sun.tools.javac.jvm.Target;
  48 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  49 import com.sun.tools.javac.tree.JCTree;
  50 import com.sun.tools.javac.tree.JCTree.JCBinary;
  51 import com.sun.tools.javac.tree.JCTree.JCConditional;
  52 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
  53 import com.sun.tools.javac.tree.JCTree.JCIdent;
  54 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
  55 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
  56 import com.sun.tools.javac.tree.JCTree.JCUnary;
  57 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
  58 import com.sun.tools.javac.tree.TreeInfo;
  59 import com.sun.tools.javac.tree.TreeMaker;
  60 import com.sun.tools.javac.tree.TreeScanner;
  61 import com.sun.tools.javac.tree.TreeTranslator;
  62 import com.sun.tools.javac.util.Constables;
  63 import com.sun.tools.javac.util.Context;
  64 import com.sun.tools.javac.util.List;
  65 import com.sun.tools.javac.util.ListBuffer;
  66 import com.sun.tools.javac.util.Log;
  67 import com.sun.tools.javac.util.Name;
  68 import com.sun.tools.javac.util.Names;
  69 import com.sun.tools.javac.util.Options;
  70 
  71 import static com.sun.tools.javac.code.Kinds.Kind.VAR;
  72 import static com.sun.tools.javac.code.TypeTag.NONE;
  73 import static com.sun.tools.javac.main.Option.G;
  74 import static com.sun.tools.javac.main.Option.G_CUSTOM;
  75 
  76 /**
  77  *  <p><b>This is NOT part of any supported API.
  78  *  If you write code that depends on this, you do so at your own risk.
  79  *  This code and its internal interfaces are subject to change or
  80  *  deletion without notice.</b>
  81  */
  82 public class ConstablesVisitor extends TreeScanner {
  83     protected static final Context.Key<ConstablesVisitor> constablesVisitorKey = new Context.Key<>();
  84 
  85     public static ConstablesVisitor instance(Context context) {
  86         ConstablesVisitor instance = context.get(constablesVisitorKey);
  87         if (instance == null)
  88             instance = new ConstablesVisitor(context);
  89         return instance;
  90     }
  91 
  92     private final Symtab syms;
  93     private final Names names;
  94     private final Types types;
  95     private final Log log;
  96     private final ConstFold cfolder;
  97     private final Constables constables;
  98     private final boolean varDebugInfo;
  99     private final TreeMaker make;
 100 
 101     protected ConstablesVisitor(Context context) {
 102         context.put(constablesVisitorKey, this);
 103         Options options = Options.instance(context);
 104         // format: -XDfolding=true, which is the default, or -XDfolding=false
 105         String foldingOp = options.get("folding");
 106         Source source = Source.instance(context);
 107         doConstantFold = foldingOp != null ?
 108                 foldingOp.equals("true") :
 109                 Feature.CONSTABLES.allowedInSource(source);
 110         syms = Symtab.instance(context);
 111         names = Names.instance(context);
 112         types = Types.instance(context);
 113         log = Log.instance(context);
 114         cfolder = ConstFold.instance(context);
 115         make = TreeMaker.instance(context);
 116         constables = Constables.instance(context);
 117         varDebugInfo =
 118             options.isUnset(G_CUSTOM)
 119             ? options.isSet(G)
 120             : options.isSet(G_CUSTOM, "vars");
 121         elementToConstantMap = new HashMap<>();
 122     }
 123 
 124     /**
 125      * Switch: fold special constants
 126      */
 127     private final boolean doConstantFold;
 128 
 129     private Env<AttrContext> attrEnv;
 130 
 131     public Map<Object, Object> elementToConstantMap;
 132 
 133     public JCTree analyzeTree(JCTree tree, Env<AttrContext> attrEnv) {
 134         try {
 135             if (!doConstantFold) {
 136                 return tree;
 137             }
 138             this.attrEnv = attrEnv;
 139             errorForLDCAndIndy = false;
 140             scan(tree);
 141             errorForLDCAndIndy = true;
 142             scan(tree);
 143             if (!varDebugInfo) {
 144                 tree = constablesSetter.translate(tree);
 145             }
 146             return tree;
 147         } finally {
 148             elementToConstantMap.clear();
 149         }
 150     }
 151 
 152     boolean errorForLDCAndIndy = false;
 153 
 154     @Override
 155     public void visitVarDef(JCVariableDecl tree) {
 156         super.visitVarDef(tree);
 157         if (tree.init != null) {
 158             VarSymbol v = tree.sym;
 159             if (elementToConstantMap.get(v) != null) {
 160                 return;
 161             }
 162             Object constant = getConstant(tree.init, v.type);
 163             if (constant != null &&
 164                     (v.isFinal() || v.isEffectivelyFinal())) {
 165                 elementToConstantMap.remove(tree.init);
 166                 elementToConstantMap.put(v, constant);
 167             }
 168         }
 169     }
 170 
 171     Object getConstant(JCTree tree) {
 172         Symbol sym = TreeInfo.symbol(tree);
 173         Object result = tree.type.constValue() != null ?
 174                 tree.type.constValue() :
 175                 elementToConstantMap.get(tree);
 176         return result == null ? elementToConstantMap.get(sym) : result;
 177     }
 178 
 179     // this one coerces
 180     Object getConstant(JCTree tree, Type targetType) {
 181         Symbol sym = TreeInfo.symbol(tree);
 182         Object result;
 183         if (tree.type.constValue() != null) {
 184             if (tree.type.isNumeric() && targetType.isNumeric()) {
 185                 result = cfolder.coerce(tree.type, targetType).constValue();
 186             } else {
 187                 result = tree.type.constValue();
 188             }
 189         } else {
 190             result = elementToConstantMap.get(tree);
 191         }
 192         return result == null ? elementToConstantMap.get(sym) : result;
 193     }
 194 
 195     @Override
 196     public void visitBinary(JCBinary tree) {
 197         super.visitBinary(tree);
 198         if (elementToConstantMap.get(tree) == null &&
 199                 tree.type.constValue() == null &&
 200                 getConstant(tree.lhs) != null &&
 201                 getConstant(tree.rhs) != null) {
 202             Object constant = cfolder.fold2(tree.operator, getConstant(tree.lhs), getConstant(tree.rhs));
 203             if (constant != null) {
 204                 elementToConstantMap.put(tree, constant);
 205             }
 206         }
 207     }
 208 
 209     @Override
 210     public void visitUnary(JCUnary tree) {
 211         super.visitUnary(tree);
 212         Object constant;
 213         if (elementToConstantMap.get(tree) == null &&
 214                 tree.type.constValue() == null &&
 215                 (constant = getConstant(tree.arg)) != null &&
 216                 constant instanceof Number) {
 217             constant = cfolder.fold1(tree.operator, constant);
 218             if (constant != null) {
 219                 elementToConstantMap.put(tree, constant);
 220             }
 221         }
 222     }
 223 
 224     @Override
 225     public void visitConditional(JCConditional tree) {
 226         super.visitConditional(tree);
 227         if (elementToConstantMap.get(tree) != null) {
 228             return;
 229         }
 230         Object condConstant = getConstant(tree.cond);
 231         Object truePartConstant = getConstant(tree.truepart);
 232         Object falsePartConstant = getConstant(tree.falsepart);
 233         if (tree.type.constValue() == null &&
 234             condConstant != null &&
 235             truePartConstant != null &&
 236             falsePartConstant != null &&
 237             !tree.type.hasTag(NONE)) {
 238             Object constant = ConstFold.isTrue(tree.cond.type.getTag(), condConstant) ?
 239                     getConstant(tree.truepart, tree.type) :
 240                     getConstant(tree.falsepart, tree.type);
 241             elementToConstantMap.put(tree, constant);
 242         }
 243         if (condConstant != null) {
 244             elementToConstantMap.put(tree.cond, condConstant);
 245         }
 246     }
 247 
 248     @Override
 249     public void visitTypeCast(JCTypeCast tree) {
 250         super.visitTypeCast(tree);
 251         if (elementToConstantMap.get(tree) == null &&
 252                 tree.type.constValue() == null &&
 253                 getConstant(tree.expr) != null) {
 254             elementToConstantMap.put(tree, getConstant(tree.expr, tree.type));
 255         }
 256     }
 257 
 258     @Override
 259     public void visitIdent(JCIdent tree) {
 260         super.visitIdent(tree);
 261         checkForSymbolConstant(tree);
 262     }
 263 
 264     void checkForSymbolConstant(JCTree tree) {
 265         if (elementToConstantMap.get(tree) != null) {
 266             return;
 267         }
 268         Symbol sym = TreeInfo.symbol(tree);
 269         if (sym != null && sym.kind == VAR) {
 270             VarSymbol v = (VarSymbol)sym;
 271             Object constant = v.getConstValue();
 272             if (constant != null && tree.type.constValue() == null) {
 273                 // we are seeing an effectively final variable which has just being assigned a
 274                 // legacy constant, we should update the type of the tree if we want Gen to generate
 275                 // the right code for us
 276                 tree.type = tree.type.constType(constant);
 277             } else {
 278                 constant = elementToConstantMap.get(v);
 279                 constant = constant != null ?
 280                         constant :
 281                         constables.foldTrackableField(tree, attrEnv);
 282                 if (constant != null) {
 283                     elementToConstantMap.put(tree, constant);
 284                 }
 285             }
 286         }
 287     }
 288 
 289     @Override
 290     public void visitSelect(JCFieldAccess tree) {
 291         super.visitSelect(tree);
 292         checkForSymbolConstant(tree);
 293     }
 294 
 295     @Override
 296     public void visitApply(JCMethodInvocation tree) {
 297         super.visitApply(tree);
 298         if (elementToConstantMap.get(tree) != null) {
 299             return;
 300         }
 301         Name methName = TreeInfo.name(tree.meth);
 302         boolean isConstructorCall = methName == names._this || methName == names._super;
 303         if (!isConstructorCall) {
 304             Object constant = constables.foldMethodInvocation(tree, attrEnv);
 305             Symbol msym = TreeInfo.symbol(tree.meth);
 306             boolean isLDC = constables.isIntrinsicsLDCInvocation(msym);
 307             if (constant == null && isLDC) {
 308                 if (errorForLDCAndIndy) {
 309                     log.error(tree.pos(), Errors.IntrinsicsLdcMustHaveConstantArg);
 310                 } else {
 311                     return;
 312                 }
 313             }
 314             if (constant != null) {
 315                 if (!isLDC) {
 316                     elementToConstantMap.put(tree, constant);
 317                 }
 318                 if (isLDC) {
 319                     Symbol sym = TreeInfo.symbol(tree.meth);
 320                     if (sym instanceof IntrinsicsLDCMethodSymbol) {
 321                         return;
 322                     }
 323                     Type newType;
 324                     // if condy
 325                     if (constables.dynamicConstantClass.isInstance(constant)) {
 326                         constant = constables.convertConstant(tree, attrEnv,
 327                                 constant, attrEnv.enclClass.sym.packge().modle);
 328                         newType = ((Pool.DynamicVariable)constant).type;
 329                     } else {
 330                         newType = tree.meth.type.asMethodType().restype;
 331                         Type unboxed = types.unboxedType(newType);
 332                         newType = unboxed != Type.noType ? unboxed : newType;
 333                         constant = constables.convertConstant(tree, attrEnv,
 334                                 constant, attrEnv.enclClass.sym.packge().modle);
 335                     }
 336                     MethodType oldMT = tree.meth.type.asMethodType();
 337                     MethodType newMT = new MethodType(oldMT.argtypes, newType, oldMT.thrown, syms.methodClass);
 338                     IntrinsicsLDCMethodSymbol ldcSymbol = new IntrinsicsLDCMethodSymbol(
 339                             msym.flags_field, msym.name, newMT, msym.owner, constant);
 340                     TreeInfo.setSymbol(tree.meth, ldcSymbol);
 341                     tree.meth.type = newMT;
 342                     tree.type = newMT.restype;
 343                 }
 344             } else if (constables.isIntrinsicsIndy(tree.meth)) {
 345                 List<Object> constants = constables.extractAllConstansOrNone(List.of(tree.args.head));
 346                 if (constants.isEmpty()) {
 347                     if (errorForLDCAndIndy) {
 348                         log.error(tree.args.head.pos(), Errors.IntrinsicsIndyMustHaveConstantArg);
 349                     }
 350                 } else {
 351                     Symbol sym = TreeInfo.symbol(tree.meth);
 352                     if (sym instanceof DynamicMethodSymbol) {
 353                         return;
 354                     }
 355                     Object indyRef = constants.head;
 356                     String invocationName = (String)constables.invokeMethodReflectively(constables.dynamicCallsiteRefClass,
 357                             indyRef, "invocationName");
 358                     if (invocationName.isEmpty()) {
 359                         log.error(tree.args.tail.head.pos(), Errors.InvocationNameCannotBeEmpty);
 360                     }
 361                     Object mh = constables.invokeMethodReflectively(constables.dynamicCallsiteRefClass,
 362                             indyRef, "bootstrapMethod");
 363                     Pool.MethodHandle mHandle = (Pool.MethodHandle)constables
 364                             .convertConstant(tree, attrEnv, mh, attrEnv.enclClass.sym.packge().modle);
 365                     boolean correct = false;
 366                     if (mHandle.refKind == 6 || mHandle.refKind == 8) {
 367                         MethodSymbol ms = (MethodSymbol)mHandle.refSym;
 368                         MethodType mt = (MethodType)ms.type;
 369                         correct = (mt.argtypes.size() >= 3 &&
 370                             mt.argtypes.head.tsym == syms.methodHandlesLookupType.tsym &&
 371                             mt.argtypes.tail.head.tsym == syms.stringType.tsym &&
 372                             mt.argtypes.tail.tail.head.tsym == syms.methodTypeType.tsym);
 373                     }
 374                     if (!correct) {
 375                         log.error(tree.args.head.pos(), Errors.MethodHandleNotSuitableIndy(mHandle.refSym.type));
 376                     }
 377 
 378                     ListBuffer<Type> arguments = new ListBuffer<>();
 379                     tree.args = tree.args.tail;
 380                     tree.args.forEach(arg -> arguments.add(arg.type));
 381                     Object[] bsmArgs = (Object[])constables.invokeMethodReflectively(constables.dynamicCallsiteRefClass, indyRef, "bootstrapArgs");
 382                     Object[] convertedBsmArgs = constables.convertConstants(tree, attrEnv, bsmArgs, attrEnv.enclClass.sym.packge().modle, true);
 383                     Object mt = constables.invokeMethodReflectively(constables.dynamicCallsiteRefClass, indyRef, "invocationType");
 384                     String methodTypeDesc = (String)constables.invokeMethodReflectively(
 385                             constables.methodTypeRefClass, mt, "descriptorString");
 386                     MethodType mType = (MethodType)constables.descriptorToType(methodTypeDesc,
 387                             attrEnv.enclClass.sym.packge().modle, true);
 388                     DynamicMethodSymbol dynSym = new DynamicMethodSymbol(
 389                             names.fromString(invocationName),
 390                             syms.noSymbol,
 391                             mHandle.refKind,
 392                             (MethodSymbol)mHandle.refSym,
 393                             mType,
 394                             convertedBsmArgs);
 395                     TreeInfo.setSymbol(tree.meth, dynSym);
 396                     tree.meth.type = mType;
 397                     // we need to issue a warning if the type of the indy is not assignable to the type of the
 398                     // tree, same for condy
 399                     tree.type = mType.restype;
 400                     tree.varargsElement = null;
 401                 }
 402             }
 403         }
 404     }
 405 
 406     ConstablesSetter constablesSetter = new ConstablesSetter();
 407     class ConstablesSetter extends TreeTranslator {
 408         @Override
 409         public void visitVarDef(JCVariableDecl tree) {
 410             super.visitVarDef(tree);
 411             tree = (JCVariableDecl)result;
 412             if (tree.init != null) {
 413                 VarSymbol v = tree.sym;
 414                 Object constant = elementToConstantMap.get(v);
 415                 if (constant != null) {
 416                     v.setData(constant);
 417                 }
 418             }
 419             result = tree;
 420         }
 421 
 422         Set<JCTree.Tag> treesToCheck = EnumSet.of(
 423                 JCTree.Tag.SELECT,
 424                 JCTree.Tag.APPLY,
 425                 JCTree.Tag.IDENT,
 426                 JCTree.Tag.CONDEXPR,
 427                 JCTree.Tag.TYPECAST
 428         );
 429 
 430         @SuppressWarnings("unchecked")
 431         @Override
 432         public <T extends JCTree> T translate(T tree) {
 433             tree = super.translate(tree);
 434             Object constant = elementToConstantMap.get(tree);
 435             if (tree != null &&
 436                     treesToCheck.contains(tree.getTag()) &&
 437                     constant != null) {
 438                 Optional<DynamicVarSymbol> opDynSym = constables.getDynamicFieldSymbol(tree, constant, attrEnv);
 439                 if (opDynSym.isPresent()) {
 440                     DynamicVarSymbol dynSym = opDynSym.get();
 441                     JCTree ident = make.at(tree.pos()).Ident(dynSym);
 442                     ident.type = dynSym.type.constType(constant);
 443                     return (T)ident;
 444                 } else {
 445                     if (constables.canMakeItToConstantValue(tree.type)) {
 446                         tree.type = tree.type.constType(constant);
 447                     }
 448                 }
 449             }
 450             return tree;
 451         }
 452 
 453         @Override
 454         public void visitTree(JCTree tree) {
 455             result = tree;
 456         }
 457     }
 458 }