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.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 
  47     /**
  48      * Creates a new {@link VirtualObject} for the given type, with the given fields. If
  49      * {@code type} is an instance class then {@code values} provides the values for the fields
  50      * returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If
  51      * {@code type} is an array then the length of the values array determines the reallocated array
  52      * length.
  53      *
  54      * @param type the type of the object whose allocation was removed during compilation. This can
  55      *            be either an instance of an array type.
  56      * @param id a unique id that identifies the object within the debug information for one
  57      *            position in the compiled code.
  58      * @return a new {@link VirtualObject} instance.
  59      */
  60     public static VirtualObject get(ResolvedJavaType type, int id) {
  61         return new VirtualObject(type, id);
  62     }
  63 
  64     private VirtualObject(ResolvedJavaType type, int id) {
  65         this.type = type;
  66         this.id = id;
  67     }
  68 
  69     private static StringBuilder appendValue(StringBuilder buf, JavaValue value, Set<VirtualObject> visited) {
  70         if (value instanceof VirtualObject) {
  71             VirtualObject vo = (VirtualObject) value;
  72             buf.append("vobject:").append(vo.type.toJavaName(false)).append(':').append(vo.id);
  73             if (!visited.contains(vo)) {
  74                 visited.add(vo);
  75                 buf.append('{');
  76                 if (vo.values == null) {
  77                     buf.append("<uninitialized>");
  78                 } else {
  79                     if (vo.type.isArray()) {
  80                         for (int i = 0; i < vo.values.length; i++) {
  81                             if (i != 0) {
  82                                 buf.append(',');
  83                             }
  84                             buf.append(i).append('=');
  85                             appendValue(buf, vo.values[i], visited);
  86                         }
  87                     } else {
  88                         ResolvedJavaField[] fields = vo.type.getInstanceFields(true);
  89                         assert fields.length == vo.values.length : vo.type + ", fields=" + Arrays.toString(fields) + ", values=" + Arrays.toString(vo.values);
  90                         for (int i = 0; i < vo.values.length; i++) {
  91                             if (i != 0) {
  92                                 buf.append(',');
  93                             }
  94                             buf.append(fields[i].getName()).append('=');
  95                             appendValue(buf, vo.values[i], visited);
  96                         }
  97                     }
  98                 }
  99                 buf.append('}');
 100             }
 101         } else {
 102             buf.append(value);
 103         }
 104         return buf;
 105     }
 106 
 107     @Override
 108     public String toString() {
 109         Set<VirtualObject> visited = Collections.newSetFromMap(new IdentityHashMap<VirtualObject, Boolean>());
 110         return appendValue(new StringBuilder(), this, visited).toString();
 111     }
 112 
 113     /**
 114      * Returns the type of the object whose allocation was removed during compilation. This can be
 115      * either an instance of an array type.
 116      */
 117     public ResolvedJavaType getType() {
 118         return type;
 119     }
 120 
 121     /**
 122      * Returns an array containing all the values to be stored into the object when it is recreated.
 123      */
 124     public JavaValue[] getValues() {
 125         return values;
 126     }
 127 
 128     /**
 129      * Returns an array containing the Java kind of all values in the object.
 130      */
 131     public JavaKind[] getSlotKinds() {
 132         return slotKinds;
 133     }
 134 
 135     /**
 136      * Returns the unique id that identifies the object within the debug information for one
 137      * position in the compiled code.
 138      */
 139     public int getId() {
 140         return id;
 141     }
 142 
 143     /**
 144      * Overwrites the current set of values with a new one.
 145      *
 146      * @param values an array containing all the values to be stored into the object when it is
 147      *            recreated.
 148      * @param slotKinds an array containing the Java kinds of the values.
 149      */
 150     public void setValues(JavaValue[] values, JavaKind[] slotKinds) {
 151         this.values = values;
 152         this.slotKinds = slotKinds;
 153     }
 154 
 155     @Override
 156     public int hashCode() {
 157         return 42 + type.hashCode();
 158     }
 159 
 160     @Override
 161     public boolean equals(Object o) {
 162         if (o == this) {
 163             return true;
 164         }
 165         if (o instanceof VirtualObject) {
 166             VirtualObject l = (VirtualObject) o;
 167             if (!l.type.equals(type) || l.values.length != values.length) {
 168                 return false;
 169             }
 170             for (int i = 0; i < values.length; i++) {
 171                 /*
 172                  * Virtual objects can form cycles. Calling equals() could therefore lead to
 173                  * infinite recursion.
 174                  */
 175                 if (!same(values[i], l.values[i])) {
 176                     return false;
 177                 }
 178             }
 179             return true;
 180         }
 181         return false;
 182     }
 183 
 184     private static boolean same(Object o1, Object o2) {
 185         return o1 == o2;
 186     }
 187 }