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 }