1 /*
   2  * Copyright (c) 2010, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.code;
  24 
  25 import java.util.Arrays;
  26 import java.util.Collections;
  27 import java.util.IdentityHashMap;
  28 import java.util.Set;
  29 
  30 import jdk.vm.ci.meta.JavaKind;
  31 import jdk.vm.ci.meta.JavaValue;
  32 import jdk.vm.ci.meta.ResolvedJavaField;
  33 import jdk.vm.ci.meta.ResolvedJavaType;
  34 
  35 /**
  36  * An instance of this class represents an object whose allocation was removed by escape analysis.
  37  * The information stored in the {@link VirtualObject} is used during deoptimization to recreate the
  38  * object.
  39  */
  40 public final class VirtualObject implements JavaValue {
  41 
  42     private final ResolvedJavaType type;
  43     private JavaValue[] values;
  44     private JavaKind[] slotKinds;
  45     private final int id;
  46     private boolean isAutoBox;
  47 
  48     /**
  49      * Creates a new {@link VirtualObject} for the given type, with the given fields. If
  50      * {@code type} is an instance class then {@code values} provides the values for the fields
  51      * returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If
  52      * {@code type} is an array then the length of the values array determines the reallocated array
  53      * length.
  54      *
  55      * @param type the type of the object whose allocation was removed during compilation. This can
  56      *            be either an instance of an array type.
  57      * @param id a unique id that identifies the object within the debug information for one
  58      *            position in the compiled code.
  59      * @return a new {@link VirtualObject} instance.
  60      */
  61     public static VirtualObject get(ResolvedJavaType type, int id) {
  62         return new VirtualObject(type, id, false);
  63     }
  64 
  65     /**
  66      * Creates a new {@link VirtualObject} for the given type, with the given fields. If
  67      * {@code type} is an instance class then {@code values} provides the values for the fields
  68      * returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If
  69      * {@code type} is an array then the length of the values array determines the reallocated array
  70      * length.
  71      *
  72      * @param type the type of the object whose allocation was removed during compilation. This can
  73      *            be either an instance of an array type.
  74      * @param id a unique id that identifies the object within the debug information for one
  75      *            position in the compiled code.
  76      * @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and
  77      *            that it possibly needs to be obtained for the box cache instead of creating
  78      *            a new instance.
  79      * @return a new {@link VirtualObject} instance.
  80      */
  81     public static VirtualObject get(ResolvedJavaType type, int id, boolean isAutoBox) {
  82         return new VirtualObject(type, id, isAutoBox);
  83     }
  84 
  85     private VirtualObject(ResolvedJavaType type, int id, boolean isAutoBox) {
  86         this.type = type;
  87         this.id = id;
  88         this.isAutoBox = isAutoBox;
  89     }
  90 
  91     private static StringBuilder appendValue(StringBuilder buf, JavaValue value, Set<VirtualObject> visited) {
  92         if (value instanceof VirtualObject) {
  93             VirtualObject vo = (VirtualObject) value;
  94             buf.append("vobject:").append(vo.type.toJavaName(false)).append(':').append(vo.id);
  95             if (!visited.contains(vo)) {
  96                 visited.add(vo);
  97                 buf.append('{');
  98                 if (vo.values == null) {
  99                     buf.append("<uninitialized>");
 100                 } else {
 101                     if (vo.type.isArray()) {
 102                         for (int i = 0; i < vo.values.length; i++) {
 103                             if (i != 0) {
 104                                 buf.append(',');
 105                             }
 106                             buf.append(i).append('=');
 107                             appendValue(buf, vo.values[i], visited);
 108                         }
 109                     } else {
 110                         ResolvedJavaField[] fields = vo.type.getInstanceFields(true);
 111                         assert fields.length == vo.values.length : vo.type + ", fields=" + Arrays.toString(fields) + ", values=" + Arrays.toString(vo.values);
 112                         for (int i = 0; i < vo.values.length; i++) {
 113                             if (i != 0) {
 114                                 buf.append(',');
 115                             }
 116                             buf.append(fields[i].getName()).append('=');
 117                             appendValue(buf, vo.values[i], visited);
 118                         }
 119                     }
 120                 }
 121                 buf.append('}');
 122             }
 123         } else {
 124             buf.append(value);
 125         }
 126         return buf;
 127     }
 128 
 129     @Override
 130     public String toString() {
 131         Set<VirtualObject> visited = Collections.newSetFromMap(new IdentityHashMap<VirtualObject, Boolean>());
 132         return appendValue(new StringBuilder(), this, visited).toString();
 133     }
 134 
 135     /**
 136      * Returns the type of the object whose allocation was removed during compilation. This can be
 137      * either an instance of an array type.
 138      */
 139     public ResolvedJavaType getType() {
 140         return type;
 141     }
 142 
 143     /**
 144      * Returns the array containing all the values to be stored into the object when it is
 145      * recreated. This field is intentional exposed as a mutable array that a compiler may modify
 146      * (e.g. during register allocation).
 147      */
 148     @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "`values` is intentional mutable")//
 149     public JavaValue[] getValues() {
 150         return values;
 151     }
 152 
 153     /**
 154      * Returns the kind of the value at {@code index}.
 155      */
 156     public JavaKind getSlotKind(int index) {
 157         return slotKinds[index];
 158     }
 159 
 160     /**
 161      * Returns the unique id that identifies the object within the debug information for one
 162      * position in the compiled code.
 163      */
 164     public int getId() {
 165         return id;
 166     }
 167 
 168     /**
 169      * Returns true if the object is a box. For boxes the deoptimization would check if the value of
 170      * the box is in the cache range and try to return a cached object.
 171      */
 172     public boolean isAutoBox() {
 173       return isAutoBox;
 174     }
 175 
 176     /**
 177      * Sets the value of the box flag.
 178      * @param isAutoBox a flag that tells the runtime that the object may be a boxed primitive and that
 179      *            it possibly needs to be obtained for the box cache instead of creating a new instance.
 180      */
 181     public void setIsAutoBox(boolean isAutoBox) {
 182       this.isAutoBox = isAutoBox;
 183     }
 184 
 185     /**
 186      * Overwrites the current set of values with a new one.
 187      *
 188      * @param values an array containing all the values to be stored into the object when it is
 189      *            recreated.
 190      * @param slotKinds an array containing the Java kinds of the values. This must have the same
 191      *            length as {@code values}. This array is now owned by this object and must not be
 192      *            mutated by the caller.
 193      */
 194     @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "caller transfers ownership of `slotKinds`")
 195     public void setValues(JavaValue[] values, JavaKind[] slotKinds) {
 196         assert values.length == slotKinds.length;
 197         this.values = values;
 198         this.slotKinds = slotKinds;
 199     }
 200 
 201     @Override
 202     public int hashCode() {
 203         return 42 + type.hashCode();
 204     }
 205 
 206     @Override
 207     public boolean equals(Object o) {
 208         if (o == this) {
 209             return true;
 210         }
 211         if (o instanceof VirtualObject) {
 212             VirtualObject l = (VirtualObject) o;
 213             if (!l.type.equals(type) || l.values.length != values.length) {
 214                 return false;
 215             }
 216             for (int i = 0; i < values.length; i++) {
 217                 /*
 218                  * Virtual objects can form cycles. Calling equals() could therefore lead to
 219                  * infinite recursion.
 220                  */
 221                 if (!same(values[i], l.values[i])) {
 222                     return false;
 223                 }
 224             }
 225             return true;
 226         }
 227         return false;
 228     }
 229 
 230     private static boolean same(Object o1, Object o2) {
 231         return o1 == o2;
 232     }
 233 }