1 /* 2 * Copyright (c) 2016, 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 org.graalvm.compiler.replacements.classfile; 24 25 import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD; 26 import static org.graalvm.compiler.bytecode.Bytecodes.GETSTATIC; 27 import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD; 28 import static org.graalvm.compiler.bytecode.Bytecodes.PUTSTATIC; 29 30 import java.io.DataInputStream; 31 import java.io.IOException; 32 33 import org.graalvm.compiler.bytecode.Bytecodes; 34 import org.graalvm.compiler.debug.GraalError; 35 36 import jdk.vm.ci.meta.JavaConstant; 37 import jdk.vm.ci.meta.ResolvedJavaField; 38 import jdk.vm.ci.meta.ResolvedJavaMethod; 39 import jdk.vm.ci.meta.ResolvedJavaType; 40 41 abstract class ClassfileConstant { 42 43 // @formatter:off 44 public static final byte CONSTANT_Utf8 = 1; 45 public static final byte CONSTANT_Integer = 3; 46 public static final byte CONSTANT_Float = 4; 47 public static final byte CONSTANT_Long = 5; 48 public static final byte CONSTANT_Double = 6; 49 public static final byte CONSTANT_Class = 7; 50 public static final byte CONSTANT_Fieldref = 9; 51 public static final byte CONSTANT_String = 8; 52 public static final byte CONSTANT_Methodref = 10; 53 public static final byte CONSTANT_InterfaceMethodref = 11; 54 public static final byte CONSTANT_NameAndType = 12; 55 public static final byte CONSTANT_MethodHandle = 15; 56 public static final byte CONSTANT_MethodType = 16; 57 public static final byte CONSTANT_InvokeDynamic = 18; 58 // @formatter:on 59 60 final byte tag; 61 62 ClassfileConstant(byte tag) { 63 this.tag = tag; 64 } 65 66 /** 67 * Loads the type, if any, referenced at a specified entry. 68 * 69 * @param cp 70 * @param index 71 * @param opcode 72 */ 73 public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { 74 } 75 76 @Override 77 public String toString() { 78 return getClass().getSimpleName(); 79 } 80 81 static class ClassRef extends ClassfileConstant { 82 83 final int nameIndex; 84 private ResolvedJavaType type; 85 86 ClassRef(DataInputStream stream) throws IOException { 87 super(CONSTANT_Class); 88 this.nameIndex = stream.readUnsignedShort(); 89 } 90 91 @Override 92 public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { 93 resolve(cp); 94 } 95 96 public ResolvedJavaType resolve(ClassfileConstantPool cp) throws GraalError { 97 if (type == null) { 98 String typeDescriptor = cp.get(Utf8.class, nameIndex).value; 99 ClassfileBytecodeProvider context = cp.context; 100 type = context.metaAccess.lookupJavaType(context.resolveToClass(typeDescriptor)); 101 } 102 return type; 103 } 104 } 105 106 static class MemberRef extends ClassfileConstant { 107 108 final int classIndex; 109 final int nameAndTypeIndex; 110 111 MemberRef(byte tag, DataInputStream stream) throws IOException { 112 super(tag); 113 this.classIndex = stream.readUnsignedShort(); 114 this.nameAndTypeIndex = stream.readUnsignedShort(); 115 } 116 117 @Override 118 public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { 119 cp.get(ClassRef.class, classIndex).loadReferencedType(cp, classIndex, opcode); 120 } 121 } 122 123 static class ExecutableRef extends MemberRef { 124 private ResolvedJavaMethod method; 125 126 ExecutableRef(byte tag, DataInputStream stream) throws IOException { 127 super(tag, stream); 128 } 129 130 ResolvedJavaMethod resolve(ClassfileConstantPool cp, int opcode) { 131 if (method == null) { 132 ResolvedJavaType cls = cp.get(ClassRef.class, classIndex).resolve(cp); 133 NameAndType nameAndType = cp.get(NameAndType.class, nameAndTypeIndex); 134 String name = nameAndType.getName(cp); 135 String type = nameAndType.getType(cp); 136 137 if (opcode == Bytecodes.INVOKEINTERFACE) { 138 method = resolveMethod(cls, name, type, false); 139 if (method == null) { 140 throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); 141 } 142 if (!method.isPublic() || !(method.getDeclaringClass().isInterface() || method.getDeclaringClass().isJavaLangObject())) { 143 throw new IncompatibleClassChangeError("cannot invokeinterface " + method.format("%H.%n(%P)%R")); 144 } 145 } else if (opcode == Bytecodes.INVOKEVIRTUAL || opcode == Bytecodes.INVOKESPECIAL) { 146 method = resolveMethod(cls, name, type, false); 147 if (method == null) { 148 throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); 149 } 150 } else { 151 assert opcode == Bytecodes.INVOKESTATIC; 152 method = resolveMethod(cls, name, type, true); 153 if (method == null) { 154 throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); 155 } 156 } 157 } 158 return method; 159 } 160 } 161 162 static class MethodRef extends ExecutableRef { 163 164 MethodRef(DataInputStream stream) throws IOException { 165 super(CONSTANT_Methodref, stream); 166 } 167 } 168 169 static class InterfaceMethodRef extends ExecutableRef { 170 InterfaceMethodRef(DataInputStream stream) throws IOException { 171 super(CONSTANT_InterfaceMethodref, stream); 172 } 173 } 174 175 static class FieldRef extends MemberRef { 176 private ResolvedJavaField field; 177 178 FieldRef(DataInputStream stream) throws IOException { 179 super(CONSTANT_Fieldref, stream); 180 } 181 182 ResolvedJavaField resolve(ClassfileConstantPool cp, int opcode) { 183 if (field == null) { 184 ResolvedJavaType cls = cp.get(ClassRef.class, classIndex).resolve(cp); 185 NameAndType nameAndType = cp.get(NameAndType.class, nameAndTypeIndex); 186 String name = nameAndType.getName(cp); 187 String type = nameAndType.getType(cp); 188 assert opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC : opcode; 189 field = resolveField(cls, name, type, opcode == GETSTATIC || opcode == PUTSTATIC); 190 if (field == null) { 191 throw new NoSuchFieldError(cls.toJavaName() + "." + name + " " + type); 192 } 193 } 194 return field; 195 } 196 } 197 198 static class Primitive extends ClassfileConstant { 199 final JavaConstant value; 200 201 Primitive(byte tag, JavaConstant value) { 202 super(tag); 203 this.value = value; 204 } 205 } 206 207 static class StringRef extends ClassfileConstant { 208 209 final int stringIndex; 210 JavaConstant value; 211 212 StringRef(DataInputStream stream) throws IOException { 213 super(ClassfileConstant.CONSTANT_String); 214 this.stringIndex = stream.readUnsignedShort(); 215 } 216 217 JavaConstant getValue(ClassfileConstantPool pool) { 218 if (value == null) { 219 value = pool.context.snippetReflection.forObject(pool.lookupUtf8(stringIndex)); 220 } 221 return value; 222 } 223 } 224 225 static class NameAndType extends ClassfileConstant { 226 227 final int nameIndex; 228 final int typeIndex; 229 private String name; 230 private String type; 231 232 NameAndType(DataInputStream stream) throws IOException { 233 super(ClassfileConstant.CONSTANT_NameAndType); 234 this.nameIndex = stream.readUnsignedShort(); 235 this.typeIndex = stream.readUnsignedShort(); 236 } 237 238 public String getName(ClassfileConstantPool cp) { 239 if (name == null) { 240 name = cp.get(Utf8.class, nameIndex).value; 241 } 242 return name; 243 } 244 245 public String getType(ClassfileConstantPool cp) { 246 if (type == null) { 247 type = cp.get(Utf8.class, typeIndex).value; 248 } 249 return type; 250 } 251 } 252 253 static class Utf8 extends ClassfileConstant { 254 final String value; 255 256 Utf8(String value) { 257 super(CONSTANT_Utf8); 258 this.value = value; 259 } 260 } 261 262 static class Unsupported extends ClassfileConstant { 263 final String name; 264 265 Unsupported(byte tag, String name) { 266 super(tag); 267 this.name = name; 268 } 269 270 @Override 271 public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { 272 throw new GraalError("Resolution of " + name + " constant pool entries not supported by " + ClassfileBytecodeProvider.class.getSimpleName()); 273 } 274 } 275 276 static ResolvedJavaMethod resolveMethod(ResolvedJavaType c, String name, String descriptor, boolean isStatic) { 277 ResolvedJavaMethod method = ClassfileBytecodeProvider.findMethod(c, name, descriptor, isStatic); 278 if (method != null) { 279 return method; 280 } 281 if (!c.isJavaLangObject() && !c.isInterface()) { 282 method = resolveMethod(c.getSuperclass(), name, descriptor, isStatic); 283 if (method != null) { 284 return method; 285 } 286 } 287 for (ResolvedJavaType i : c.getInterfaces()) { 288 method = resolveMethod(i, name, descriptor, isStatic); 289 if (method != null) { 290 return method; 291 } 292 } 293 return null; 294 } 295 296 static ResolvedJavaField resolveField(ResolvedJavaType c, String name, String fieldType, boolean isStatic) { 297 ResolvedJavaField field = ClassfileBytecodeProvider.findField(c, name, fieldType, isStatic); 298 if (field != null) { 299 return field; 300 } 301 if (!c.isJavaLangObject() && !c.isInterface()) { 302 field = resolveField(c.getSuperclass(), name, fieldType, isStatic); 303 if (field != null) { 304 return field; 305 } 306 } 307 for (ResolvedJavaType i : c.getInterfaces()) { 308 field = resolveField(i, name, fieldType, isStatic); 309 if (field != null) { 310 return field; 311 } 312 } 313 return null; 314 } 315 }