1 /*
   2  * Copyright (c) 2010, 2015, 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.*;
  26 
  27 import jdk.vm.ci.meta.*;
  28 
  29 /**
  30  * An instance of this class represents an object whose allocation was removed by escape analysis.
  31  * The information stored in the {@link VirtualObject} is used during deoptimization to recreate the
  32  * object.
  33  */
  34 public final class VirtualObject implements JavaValue {
  35 
  36     private final ResolvedJavaType type;
  37     private JavaValue[] values;
  38     private JavaKind[] slotKinds;
  39     private final int id;
  40 
  41     /**
  42      * Creates a new {@link VirtualObject} for the given type, with the given fields. If
  43      * {@code type} is an instance class then {@code values} provides the values for the fields
  44      * returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If
  45      * {@code type} is an array then the length of the values array determines the reallocated array
  46      * length.
  47      *
  48      * @param type the type of the object whose allocation was removed during compilation. This can
  49      *            be either an instance of an array type.
  50      * @param id a unique id that identifies the object within the debug information for one
  51      *            position in the compiled code.
  52      * @return a new {@link VirtualObject} instance.
  53      */
  54     public static VirtualObject get(ResolvedJavaType type, int id) {
  55         return new VirtualObject(type, id);
  56     }
  57 
  58     private VirtualObject(ResolvedJavaType type, int id) {
  59         this.type = type;
  60         this.id = id;
  61     }
  62 
  63     private static StringBuilder appendValue(StringBuilder buf, JavaValue value, Set<VirtualObject> visited) {
  64         if (value instanceof VirtualObject) {
  65             VirtualObject vo = (VirtualObject) value;
  66             buf.append("vobject:").append(vo.type.toJavaName(false)).append(':').append(vo.id);
  67             if (!visited.contains(vo)) {
  68                 visited.add(vo);
  69                 buf.append('{');
  70                 if (vo.values == null) {
  71                     buf.append("<uninitialized>");
  72                 } else {
  73                     if (vo.type.isArray()) {
  74                         for (int i = 0; i < vo.values.length; i++) {
  75                             if (i != 0) {
  76                                 buf.append(',');
  77                             }
  78                             buf.append(i).append('=');
  79                             appendValue(buf, vo.values[i], visited);
  80                         }
  81                     } else {
  82                         ResolvedJavaField[] fields = vo.type.getInstanceFields(true);
  83                         assert fields.length == vo.values.length : vo.type + ", fields=" + Arrays.toString(fields) + ", values=" + Arrays.toString(vo.values);
  84                         for (int i = 0; i < vo.values.length; i++) {
  85                             if (i != 0) {
  86                                 buf.append(',');
  87                             }
  88                             buf.append(fields[i].getName()).append('=');
  89                             appendValue(buf, vo.values[i], visited);
  90                         }
  91                     }
  92                 }
  93                 buf.append('}');
  94             }
  95         } else {
  96             buf.append(value);
  97         }
  98         return buf;
  99     }
 100 
 101     @Override
 102     public String toString() {
 103         Set<VirtualObject> visited = Collections.newSetFromMap(new IdentityHashMap<VirtualObject, Boolean>());
 104         return appendValue(new StringBuilder(), this, visited).toString();
 105     }
 106 
 107     /**
 108      * Returns the type of the object whose allocation was removed during compilation. This can be
 109      * either an instance of an array type.
 110      */
 111     public ResolvedJavaType getType() {
 112         return type;
 113     }
 114 
 115     /**
 116      * Returns an array containing all the values to be stored into the object when it is recreated.
 117      */
 118     public JavaValue[] getValues() {
 119         return values;
 120     }
 121 
 122     /**
 123      * Returns an array containing the Java kind of all values in the object.
 124      */
 125     public JavaKind[] getSlotKinds() {
 126         return slotKinds;
 127     }
 128 
 129     /**
 130      * Returns the unique id that identifies the object within the debug information for one
 131      * position in the compiled code.
 132      */
 133     public int getId() {
 134         return id;
 135     }
 136 
 137     private boolean checkValues() {
 138         assert (values == null) == (slotKinds == null);
 139         if (values != null) {
 140             assert values.length == slotKinds.length;
 141             if (!type.isArray()) {
 142                 ResolvedJavaField[] fields = type.getInstanceFields(true);
 143                 int fieldIndex = 0;
 144                 for (int i = 0; i < values.length; i++) {
 145                     ResolvedJavaField field = fields[fieldIndex++];
 146                     JavaKind valKind = slotKinds[i].getStackKind();
 147                     if (field.getJavaKind() == JavaKind.Object) {
 148                         assert valKind.isObject() : field + ": " + valKind + " != " + field.getJavaKind();
 149                     } else {
 150                         if ((valKind == JavaKind.Double || valKind == JavaKind.Long) && field.getJavaKind() == JavaKind.Int) {
 151                             assert fields[fieldIndex].getJavaKind() == JavaKind.Int;
 152                             fieldIndex++;
 153                         } else {
 154                             assert valKind == field.getJavaKind().getStackKind() : field + ": " + valKind + " != " + field.getJavaKind();
 155                         }
 156                     }
 157                 }
 158                 assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values);
 159             } else {
 160                 JavaKind componentKind = type.getComponentType().getJavaKind().getStackKind();
 161                 if (componentKind == JavaKind.Object) {
 162                     for (int i = 0; i < values.length; i++) {
 163                         assert slotKinds[i].isObject() : slotKinds[i] + " != " + componentKind;
 164                     }
 165                 } else {
 166                     for (int i = 0; i < values.length; i++) {
 167                         assert slotKinds[i] == componentKind || componentKind.getBitCount() >= slotKinds[i].getBitCount() ||
 168                                         (componentKind == JavaKind.Int && slotKinds[i].getBitCount() >= JavaKind.Int.getBitCount()) : slotKinds[i] + " != " + componentKind;
 169                     }
 170                 }
 171             }
 172         }
 173         return true;
 174     }
 175 
 176     /**
 177      * Overwrites the current set of values with a new one.
 178      *
 179      * @param values an array containing all the values to be stored into the object when it is
 180      *            recreated.
 181      * @param slotKinds an array containing the Java kinds of the values.
 182      */
 183     public void setValues(JavaValue[] values, JavaKind[] slotKinds) {
 184         this.values = values;
 185         this.slotKinds = slotKinds;
 186         assert checkValues();
 187     }
 188 
 189     @Override
 190     public int hashCode() {
 191         return 42 + type.hashCode();
 192     }
 193 
 194     @Override
 195     public boolean equals(Object o) {
 196         if (o == this) {
 197             return true;
 198         }
 199         if (o instanceof VirtualObject) {
 200             VirtualObject l = (VirtualObject) o;
 201             if (!l.type.equals(type) || l.values.length != values.length) {
 202                 return false;
 203             }
 204             for (int i = 0; i < values.length; i++) {
 205                 /*
 206                  * Virtual objects can form cycles. Calling equals() could therefore lead to
 207                  * infinite recursion.
 208                  */
 209                 if (!same(values[i], l.values[i])) {
 210                     return false;
 211                 }
 212             }
 213             return true;
 214         }
 215         return false;
 216     }
 217 
 218     private static boolean same(Object o1, Object o2) {
 219         return o1 == o2;
 220     }
 221 }