001/* 002 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.code; 024 025import java.util.*; 026 027import jdk.internal.jvmci.meta.*; 028 029/** 030 * An instance of this class represents an object whose allocation was removed by escape analysis. 031 * The information stored in the {@link VirtualObject} is used during deoptimization to recreate the 032 * object. 033 */ 034public final class VirtualObject extends AbstractValue implements JavaValue { 035 036 private final ResolvedJavaType type; 037 private Value[] values; 038 private final int id; 039 040 /** 041 * Creates a new {@link VirtualObject} for the given type, with the given fields. If 042 * {@code type} is an instance class then {@code values} provides the values for the fields 043 * returned by {@link ResolvedJavaType#getInstanceFields(boolean) getInstanceFields(true)}. If 044 * {@code type} is an array then the length of the values array determines the reallocated array 045 * length. 046 * 047 * @param type the type of the object whose allocation was removed during compilation. This can 048 * be either an instance of an array type. 049 * @param values an array containing all the values to be stored into the object when it is 050 * recreated 051 * @param id a unique id that identifies the object within the debug information for one 052 * position in the compiled code. 053 * @return a new {@link VirtualObject} instance. 054 */ 055 public static VirtualObject get(ResolvedJavaType type, Value[] values, int id) { 056 return new VirtualObject(type, values, id); 057 } 058 059 private VirtualObject(ResolvedJavaType type, Value[] values, int id) { 060 super(LIRKind.reference(Kind.Object)); 061 this.type = type; 062 this.values = values; 063 this.id = id; 064 } 065 066 private static StringBuilder appendValue(StringBuilder buf, Value value, Set<VirtualObject> visited) { 067 if (value instanceof VirtualObject) { 068 VirtualObject vo = (VirtualObject) value; 069 buf.append("vobject:").append(vo.type.toJavaName(false)).append(':').append(vo.id); 070 if (!visited.contains(vo)) { 071 visited.add(vo); 072 buf.append('{'); 073 if (vo.values == null) { 074 buf.append("<uninitialized>"); 075 } else { 076 if (vo.type.isArray()) { 077 for (int i = 0; i < vo.values.length; i++) { 078 if (i != 0) { 079 buf.append(','); 080 } 081 buf.append(i).append('='); 082 appendValue(buf, vo.values[i], visited); 083 } 084 } else { 085 ResolvedJavaField[] fields = vo.type.getInstanceFields(true); 086 assert fields.length == vo.values.length : vo.type + ", fields=" + Arrays.toString(fields) + ", values=" + Arrays.toString(vo.values); 087 for (int i = 0; i < vo.values.length; i++) { 088 if (i != 0) { 089 buf.append(','); 090 } 091 buf.append(fields[i].getName()).append('='); 092 appendValue(buf, vo.values[i], visited); 093 } 094 } 095 } 096 buf.append('}'); 097 } 098 } else { 099 buf.append(value); 100 } 101 return buf; 102 } 103 104 @Override 105 public String toString() { 106 Set<VirtualObject> visited = Collections.newSetFromMap(new IdentityHashMap<VirtualObject, Boolean>()); 107 return appendValue(new StringBuilder(), this, visited).toString(); 108 } 109 110 /** 111 * Returns the type of the object whose allocation was removed during compilation. This can be 112 * either an instance of an array type. 113 */ 114 public ResolvedJavaType getType() { 115 return type; 116 } 117 118 /** 119 * Returns an array containing all the values to be stored into the object when it is recreated. 120 */ 121 public Value[] getValues() { 122 return values; 123 } 124 125 /** 126 * Returns the unique id that identifies the object within the debug information for one 127 * position in the compiled code. 128 */ 129 public int getId() { 130 return id; 131 } 132 133 private static boolean checkValues(ResolvedJavaType type, Value[] values) { 134 if (values != null) { 135 if (!type.isArray()) { 136 ResolvedJavaField[] fields = type.getInstanceFields(true); 137 int fieldIndex = 0; 138 for (int i = 0; i < values.length; i++) { 139 ResolvedJavaField field = fields[fieldIndex++]; 140 Kind valKind = values[i].getKind().getStackKind(); 141 if (field.getKind() == Kind.Object) { 142 assert values[i].getLIRKind().isReference(0) : field + ": " + valKind + " != " + field.getKind(); 143 } else { 144 if ((valKind == Kind.Double || valKind == Kind.Long) && field.getKind() == Kind.Int) { 145 assert fields[fieldIndex].getKind() == Kind.Int; 146 fieldIndex++; 147 } else { 148 assert valKind == field.getKind().getStackKind() : field + ": " + valKind + " != " + field.getKind(); 149 } 150 } 151 } 152 assert fields.length == fieldIndex : type + ": fields=" + Arrays.toString(fields) + ", field values=" + Arrays.toString(values); 153 } else { 154 Kind componentKind = type.getComponentType().getKind().getStackKind(); 155 if (componentKind == Kind.Object) { 156 for (int i = 0; i < values.length; i++) { 157 assert values[i].getLIRKind().isReference(0) : values[i].getKind() + " != " + componentKind; 158 } 159 } else { 160 for (int i = 0; i < values.length; i++) { 161 assert values[i].getKind() == componentKind || componentKind.getBitCount() >= values[i].getKind().getBitCount() || 162 (componentKind == Kind.Int && values[i].getKind().getBitCount() >= Kind.Int.getBitCount()) : values[i].getKind() + " != " + componentKind; 163 } 164 } 165 } 166 } 167 return true; 168 } 169 170 /** 171 * Overwrites the current set of values with a new one. 172 * 173 * @param values an array containing all the values to be stored into the object when it is 174 * recreated. 175 */ 176 public void setValues(Value[] values) { 177 assert checkValues(type, values); 178 this.values = values; 179 } 180 181 @Override 182 public int hashCode() { 183 return getLIRKind().hashCode() + type.hashCode(); 184 } 185 186 @Override 187 public boolean equals(Object o) { 188 if (o == this) { 189 return true; 190 } 191 if (o instanceof VirtualObject) { 192 VirtualObject l = (VirtualObject) o; 193 if (!l.type.equals(type) || l.values.length != values.length) { 194 return false; 195 } 196 for (int i = 0; i < values.length; i++) { 197 /* 198 * Virtual objects can form cycles. Calling equals() could therefore lead to 199 * infinite recursion. 200 */ 201 if (!same(values[i], l.values[i])) { 202 return false; 203 } 204 } 205 return true; 206 } 207 return false; 208 } 209 210 private static boolean same(Object o1, Object o2) { 211 return o1 == o2; 212 } 213}