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 }