1 /*
   2  * Copyright (c) 2010, 2013, 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 jdk.nashorn.internal.ir;
  27 
  28 import jdk.nashorn.internal.codegen.types.Type;
  29 
  30 /**
  31  * Class describing one or more local variable conversions that needs to be performed on entry to a control flow join
  32  * point. Note that the class is named as a singular "Conversion" and not a plural "Conversions", but instances of the
  33  * class have a reference to the next conversion, so multiple conversions are always represented with a single instance
  34  * that is a head of a linked list of instances.
  35  * @see JoinPredecessor
  36  */
  37 public final class LocalVariableConversion {
  38     private final Symbol symbol;
  39     // TODO: maybe introduce a type pair class? These will often be repeated.
  40     private final Type from;
  41     private final Type to;
  42     private final LocalVariableConversion next;
  43 
  44     /**
  45      * Creates a new object representing a local variable conversion.
  46      * @param symbol the symbol representing the local variable whose value is being converted.
  47      * @param from the type value is being converted from.
  48      * @param to the type value is being converted to.
  49      * @param next next conversion at the same join point, if any (the conversion object implements a singly-linked
  50      * list of conversions).
  51      */
  52     public LocalVariableConversion(final Symbol symbol, final Type from, final Type to, final LocalVariableConversion next) {
  53         this.symbol = symbol;
  54         this.from = from;
  55         this.to = to;
  56         this.next = next;
  57     }
  58 
  59     /**
  60      * Returns the type being converted from.
  61      * @return the type being converted from.
  62      */
  63     public Type getFrom() {
  64         return from;
  65     }
  66 
  67     /**
  68      * Returns the type being converted to.
  69      * @return the type being converted to.
  70      */
  71     public Type getTo() {
  72         return to;
  73     }
  74 
  75     /**
  76      * Returns the next conversion at the same join point, or null if this is the last one.
  77      * @return the next conversion at the same join point.
  78      */
  79     public LocalVariableConversion getNext() {
  80         return next;
  81     }
  82 
  83     /**
  84      * Returns the symbol representing the local variable whose value is being converted.
  85      * @return the symbol representing the local variable whose value is being converted.
  86      */
  87     public Symbol getSymbol() {
  88         return symbol;
  89     }
  90 
  91     /**
  92      * Returns true if this conversion is live. A conversion is live if the symbol has a slot for the conversion's
  93      * {@link #getTo() to} type. If a conversion is dead, it can be omitted in code generator.
  94      * @return true if this conversion is live.
  95      */
  96     public boolean isLive() {
  97         return symbol.hasSlotFor(to);
  98     }
  99 
 100     /**
 101      * Returns true if this conversion {@link #isLive()}, or if any of its {@link #getNext()} conversions are live.
 102      * @return true if this conversion, or any conversion following it, are live.
 103      */
 104     public boolean isAnyLive() {
 105         return isLive() || isAnyLive(next);
 106     }
 107 
 108     /**
 109      * Returns true if the passed join predecessor has {@link #isAnyLive()} conversion.
 110      * @param jp the join predecessor being examined.
 111      * @return true if the join predecessor conversion is not null and {@link #isAnyLive()}.
 112      */
 113     public static boolean hasLiveConversion(final JoinPredecessor jp) {
 114         return isAnyLive(jp.getLocalVariableConversion());
 115     }
 116 
 117     /**
 118      * Returns true if the passed conversion is not null, and it {@link #isAnyLive()}.
 119      * @parameter conv the conversion being tested for liveness.
 120      * @return true if the conversion is not null and {@link #isAnyLive()}.
 121      */
 122     private static boolean isAnyLive(final LocalVariableConversion conv) {
 123         return conv != null && conv.isAnyLive();
 124     }
 125 
 126     @Override
 127     public String toString() {
 128         return toString(new StringBuilder()).toString();
 129     }
 130 
 131     /**
 132      * Generates a string representation of this conversion in the passed string builder.
 133      * @param sb the string builder in which to generate a string representation of this conversion.
 134      * @return the passed in string builder.
 135      */
 136     public StringBuilder toString(final StringBuilder sb) {
 137         if(isLive()) {
 138             return toStringNext(sb.append('\u27e6'), true).append("\u27e7 ");
 139         }
 140         return next == null ? sb : next.toString(sb);
 141     }
 142 
 143     /**
 144      * Generates a string representation of the passed conversion in the passed string builder.
 145      * @param conv the conversion to render in the string builder.
 146      * @param sb the string builder in which to generate a string representation of this conversion.
 147      * @return the passed in string builder.
 148      */
 149     public static StringBuilder toString(final LocalVariableConversion conv, final StringBuilder sb) {
 150         return conv == null ? sb : conv.toString(sb);
 151     }
 152 
 153     private StringBuilder toStringNext(final StringBuilder sb, final boolean first) {
 154         if(isLive()) {
 155             if(!first) {
 156                 sb.append(", ");
 157             }
 158             sb.append(symbol.getName()).append(':').append(getTypeChar(from)).append('\u2192').append(getTypeChar(to));
 159             return next == null ? sb : next.toStringNext(sb, false);
 160         }
 161         return next == null ? sb : next.toStringNext(sb, first);
 162     }
 163 
 164     private static char getTypeChar(final Type type) {
 165         if(type == Type.UNDEFINED) {
 166             return 'U';
 167         } else if(type.isObject()) {
 168             return 'O';
 169         } else if(type == Type.BOOLEAN) {
 170             return 'Z';
 171         }
 172         return type.getBytecodeStackType();
 173     }
 174 }