/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.graalvm.compiler.replacements.classfile; import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD; import static org.graalvm.compiler.bytecode.Bytecodes.GETSTATIC; import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD; import static org.graalvm.compiler.bytecode.Bytecodes.PUTSTATIC; import java.io.DataInputStream; import java.io.IOException; import org.graalvm.compiler.bytecode.Bytecodes; import org.graalvm.compiler.debug.GraalError; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; abstract class ClassfileConstant { // @formatter:off public static final byte CONSTANT_Utf8 = 1; public static final byte CONSTANT_Integer = 3; public static final byte CONSTANT_Float = 4; public static final byte CONSTANT_Long = 5; public static final byte CONSTANT_Double = 6; public static final byte CONSTANT_Class = 7; public static final byte CONSTANT_Fieldref = 9; public static final byte CONSTANT_String = 8; public static final byte CONSTANT_Methodref = 10; public static final byte CONSTANT_InterfaceMethodref = 11; public static final byte CONSTANT_NameAndType = 12; public static final byte CONSTANT_MethodHandle = 15; public static final byte CONSTANT_MethodType = 16; public static final byte CONSTANT_InvokeDynamic = 18; // @formatter:on final byte tag; ClassfileConstant(byte tag) { this.tag = tag; } /** * Loads the type, if any, referenced at a specified entry. * * @param cp * @param index * @param opcode */ public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { } @Override public String toString() { return getClass().getSimpleName(); } static class ClassRef extends ClassfileConstant { final int nameIndex; private ResolvedJavaType type; ClassRef(DataInputStream stream) throws IOException { super(CONSTANT_Class); this.nameIndex = stream.readUnsignedShort(); } @Override public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { resolve(cp); } public ResolvedJavaType resolve(ClassfileConstantPool cp) throws GraalError { if (type == null) { String typeDescriptor = cp.get(Utf8.class, nameIndex).value; ClassfileBytecodeProvider context = cp.context; type = context.metaAccess.lookupJavaType(context.resolveToClass(typeDescriptor)); } return type; } } static class MemberRef extends ClassfileConstant { final int classIndex; final int nameAndTypeIndex; MemberRef(byte tag, DataInputStream stream) throws IOException { super(tag); this.classIndex = stream.readUnsignedShort(); this.nameAndTypeIndex = stream.readUnsignedShort(); } @Override public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { cp.get(ClassRef.class, classIndex).loadReferencedType(cp, classIndex, opcode); } } static class ExecutableRef extends MemberRef { private ResolvedJavaMethod method; ExecutableRef(byte tag, DataInputStream stream) throws IOException { super(tag, stream); } ResolvedJavaMethod resolve(ClassfileConstantPool cp, int opcode) { if (method == null) { ResolvedJavaType cls = cp.get(ClassRef.class, classIndex).resolve(cp); NameAndType nameAndType = cp.get(NameAndType.class, nameAndTypeIndex); String name = nameAndType.getName(cp); String type = nameAndType.getType(cp); if (opcode == Bytecodes.INVOKEINTERFACE) { method = resolveMethod(cls, name, type, false); if (method == null) { throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); } if (!method.isPublic() || !(method.getDeclaringClass().isInterface() || method.getDeclaringClass().isJavaLangObject())) { throw new IncompatibleClassChangeError("cannot invokeinterface " + method.format("%H.%n(%P)%R")); } } else if (opcode == Bytecodes.INVOKEVIRTUAL || opcode == Bytecodes.INVOKESPECIAL) { method = resolveMethod(cls, name, type, false); if (method == null) { throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); } } else { assert opcode == Bytecodes.INVOKESTATIC; method = resolveMethod(cls, name, type, true); if (method == null) { throw new NoSuchMethodError(cls.toJavaName() + "." + name + type); } } } return method; } } static class MethodRef extends ExecutableRef { MethodRef(DataInputStream stream) throws IOException { super(CONSTANT_Methodref, stream); } } static class InterfaceMethodRef extends ExecutableRef { InterfaceMethodRef(DataInputStream stream) throws IOException { super(CONSTANT_InterfaceMethodref, stream); } } static class FieldRef extends MemberRef { private ResolvedJavaField field; FieldRef(DataInputStream stream) throws IOException { super(CONSTANT_Fieldref, stream); } ResolvedJavaField resolve(ClassfileConstantPool cp, int opcode) { if (field == null) { ResolvedJavaType cls = cp.get(ClassRef.class, classIndex).resolve(cp); NameAndType nameAndType = cp.get(NameAndType.class, nameAndTypeIndex); String name = nameAndType.getName(cp); String type = nameAndType.getType(cp); assert opcode == GETFIELD || opcode == GETSTATIC || opcode == PUTFIELD || opcode == PUTSTATIC : opcode; field = resolveField(cls, name, type, opcode == GETSTATIC || opcode == PUTSTATIC); if (field == null) { throw new NoSuchFieldError(cls.toJavaName() + "." + name + " " + type); } } return field; } } static class Primitive extends ClassfileConstant { final JavaConstant value; Primitive(byte tag, JavaConstant value) { super(tag); this.value = value; } } static class StringRef extends ClassfileConstant { final int stringIndex; JavaConstant value; StringRef(DataInputStream stream) throws IOException { super(ClassfileConstant.CONSTANT_String); this.stringIndex = stream.readUnsignedShort(); } JavaConstant getValue(ClassfileConstantPool pool) { if (value == null) { value = pool.context.snippetReflection.forObject(pool.lookupUtf8(stringIndex)); } return value; } } static class NameAndType extends ClassfileConstant { final int nameIndex; final int typeIndex; private String name; private String type; NameAndType(DataInputStream stream) throws IOException { super(ClassfileConstant.CONSTANT_NameAndType); this.nameIndex = stream.readUnsignedShort(); this.typeIndex = stream.readUnsignedShort(); } public String getName(ClassfileConstantPool cp) { if (name == null) { name = cp.get(Utf8.class, nameIndex).value; } return name; } public String getType(ClassfileConstantPool cp) { if (type == null) { type = cp.get(Utf8.class, typeIndex).value; } return type; } } static class Utf8 extends ClassfileConstant { final String value; Utf8(String value) { super(CONSTANT_Utf8); this.value = value; } } static class Unsupported extends ClassfileConstant { final String name; Unsupported(byte tag, String name) { super(tag); this.name = name; } @Override public void loadReferencedType(ClassfileConstantPool cp, int index, int opcode) { throw new GraalError("Resolution of " + name + " constant pool entries not supported by " + ClassfileBytecodeProvider.class.getSimpleName()); } } static ResolvedJavaMethod resolveMethod(ResolvedJavaType c, String name, String descriptor, boolean isStatic) { ResolvedJavaMethod method = ClassfileBytecodeProvider.findMethod(c, name, descriptor, isStatic); if (method != null) { return method; } if (!c.isJavaLangObject() && !c.isInterface()) { method = resolveMethod(c.getSuperclass(), name, descriptor, isStatic); if (method != null) { return method; } } for (ResolvedJavaType i : c.getInterfaces()) { method = resolveMethod(i, name, descriptor, isStatic); if (method != null) { return method; } } return null; } static ResolvedJavaField resolveField(ResolvedJavaType c, String name, String fieldType, boolean isStatic) { ResolvedJavaField field = ClassfileBytecodeProvider.findField(c, name, fieldType, isStatic); if (field != null) { return field; } if (!c.isJavaLangObject() && !c.isInterface()) { field = resolveField(c.getSuperclass(), name, fieldType, isStatic); if (field != null) { return field; } } for (ResolvedJavaType i : c.getInterfaces()) { field = resolveField(i, name, fieldType, isStatic); if (field != null) { return field; } } return null; } }