1 /*
2  * Copyright (c) 2016, 2018, 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 
24 
25 package org.graalvm.compiler.replacements.classfile;
26 
27 import static org.graalvm.compiler.replacements.classfile.Classfile.skipFully;
28 import static org.graalvm.compiler.replacements.classfile.ClassfileConstant.CONSTANT_Class;
29 
30 import java.io.DataInputStream;
31 import java.io.IOException;
32 
33 import org.graalvm.compiler.debug.GraalError;
34 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.ClassRef;
35 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.ExecutableRef;
36 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.FieldRef;
37 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Primitive;
38 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Utf8;
39 
40 import jdk.vm.ci.meta.ConstantPool;
41 import jdk.vm.ci.meta.JavaConstant;
42 import jdk.vm.ci.meta.JavaField;
43 import jdk.vm.ci.meta.JavaMethod;
44 import jdk.vm.ci.meta.JavaType;
45 import jdk.vm.ci.meta.ResolvedJavaMethod;
46 import jdk.vm.ci.meta.Signature;
47 
48 class ClassfileConstantPool implements ConstantPool, ConstantPoolPatch {
49 
50     final ClassfileConstant[] entries;
51     final ClassfileBytecodeProvider context;
52 
53     public static class Bytecodes {
54         public static final int LDC = 18; // 0x12
55         public static final int LDC_W = 19; // 0x13
56         public static final int LDC2_W = 20; // 0x14
57         public static final int GETSTATIC = 178; // 0xB2
58         public static final int PUTSTATIC = 179; // 0xB3
59         public static final int GETFIELD = 180; // 0xB4
60         public static final int PUTFIELD = 181; // 0xB5
61         public static final int INVOKEVIRTUAL = 182; // 0xB6
62         public static final int INVOKESPECIAL = 183; // 0xB7
63         public static final int INVOKESTATIC = 184; // 0xB8
64         public static final int INVOKEINTERFACE = 185; // 0xB9
65         public static final int INVOKEDYNAMIC = 186; // 0xBA
66         public static final int NEW = 187; // 0xBB
67         public static final int NEWARRAY = 188; // 0xBC
68         public static final int ANEWARRAY = 189; // 0xBD
69         public static final int CHECKCAST = 192; // 0xC0
70         public static final int INSTANCEOF = 193; // 0xC1
71         public static final int MULTIANEWARRAY = 197; // 0xC5
72     }
73 
ClassfileConstantPool(DataInputStream stream, ClassfileBytecodeProvider context)74     ClassfileConstantPool(DataInputStream stream, ClassfileBytecodeProvider context) throws IOException {
75         this.context = context;
76         byte tag;
77 
78         int count = stream.readUnsignedShort();
79         entries = new ClassfileConstant[count];
80 
81         int i = 1;
82         while (i < count) {
83             entries[i] = readConstant(stream);
84             tag = entries[i].tag;
85 
86             if ((tag == ClassfileConstant.CONSTANT_Double) || (tag == ClassfileConstant.CONSTANT_Long)) {
87                 i += 2;
88             } else {
89                 i += 1;
90             }
91         }
92     }
93 
readConstant(DataInputStream stream)94     static final ClassfileConstant readConstant(DataInputStream stream) throws IOException {
95         byte tag = stream.readByte();
96 
97         switch (tag) {
98             case ClassfileConstant.CONSTANT_Class:
99                 return new ClassfileConstant.ClassRef(stream);
100             case ClassfileConstant.CONSTANT_Fieldref:
101                 return new ClassfileConstant.FieldRef(stream);
102             case ClassfileConstant.CONSTANT_Methodref:
103                 return new ClassfileConstant.MethodRef(stream);
104             case ClassfileConstant.CONSTANT_InterfaceMethodref:
105                 return new ClassfileConstant.InterfaceMethodRef(stream);
106             case ClassfileConstant.CONSTANT_String:
107                 return new ClassfileConstant.StringRef(stream);
108             case ClassfileConstant.CONSTANT_Integer:
109                 return new ClassfileConstant.Primitive(tag, JavaConstant.forInt(stream.readInt()));
110             case ClassfileConstant.CONSTANT_Float:
111                 return new ClassfileConstant.Primitive(tag, JavaConstant.forFloat(stream.readFloat()));
112             case ClassfileConstant.CONSTANT_Long:
113                 return new ClassfileConstant.Primitive(tag, JavaConstant.forLong(stream.readLong()));
114             case ClassfileConstant.CONSTANT_Double:
115                 return new ClassfileConstant.Primitive(tag, JavaConstant.forDouble(stream.readDouble()));
116             case ClassfileConstant.CONSTANT_NameAndType:
117                 return new ClassfileConstant.NameAndType(stream);
118             case ClassfileConstant.CONSTANT_Utf8:
119                 return new ClassfileConstant.Utf8(stream.readUTF());
120             case ClassfileConstant.CONSTANT_MethodHandle:
121                 skipFully(stream, 3); // reference_kind, reference_index
122                 return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodHandle_info");
123             case ClassfileConstant.CONSTANT_MethodType:
124                 skipFully(stream, 2); // descriptor_index
125                 return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodType_info");
126             case ClassfileConstant.CONSTANT_Dynamic:
127                 skipFully(stream, 4); // bootstrap_method_attr_index, name_and_type_index
128                 return new ClassfileConstant.Unsupported(tag, "CONSTANT_Dynamic_info");
129             case ClassfileConstant.CONSTANT_InvokeDynamic:
130                 skipFully(stream, 4); // bootstrap_method_attr_index, name_and_type_index
131                 return new ClassfileConstant.Unsupported(tag, "CONSTANT_InvokeDynamic_info");
132             default:
133                 throw new GraalError("Invalid constant pool tag: " + tag);
134         }
135     }
136 
137     @Override
length()138     public int length() {
139         return entries.length;
140     }
141 
get(Class<T> c, int index)142     <T extends ClassfileConstant> T get(Class<T> c, int index) {
143         return c.cast(entries[index]);
144     }
145 
146     @Override
loadReferencedType(int index, int opcode)147     public void loadReferencedType(int index, int opcode) {
148         if (opcode == Bytecodes.INVOKEDYNAMIC) {
149             throw new GraalError("INVOKEDYNAMIC not supported by " + ClassfileBytecodeProvider.class.getSimpleName());
150         }
151         entries[index].loadReferencedType(this, index, opcode);
152     }
153 
154     @Override
lookupField(int index, ResolvedJavaMethod method, int opcode)155     public JavaField lookupField(int index, ResolvedJavaMethod method, int opcode) {
156         return get(FieldRef.class, index).resolve(this, opcode);
157     }
158 
159     @Override
lookupMethod(int index, int opcode)160     public JavaMethod lookupMethod(int index, int opcode) {
161         if (opcode == Bytecodes.INVOKEDYNAMIC) {
162             throw new GraalError("INVOKEDYNAMIC not supported by" + ClassfileBytecodeProvider.class.getSimpleName());
163         }
164         return get(ExecutableRef.class, index).resolve(this, opcode);
165     }
166 
167     @Override
lookupType(int index, int opcode)168     public JavaType lookupType(int index, int opcode) {
169         return get(ClassRef.class, index).resolve(this);
170     }
171 
172     @Override
lookupReferencedType(int index, int opcode)173     public JavaType lookupReferencedType(int index, int opcode) {
174         switch (opcode) {
175             case Bytecodes.CHECKCAST:
176             case Bytecodes.INSTANCEOF:
177             case Bytecodes.NEW:
178             case Bytecodes.ANEWARRAY:
179             case Bytecodes.MULTIANEWARRAY:
180             case Bytecodes.LDC:
181             case Bytecodes.LDC_W:
182             case Bytecodes.LDC2_W:
183                 return get(ClassRef.class, index).resolve(this);
184             case Bytecodes.GETSTATIC:
185             case Bytecodes.PUTSTATIC:
186             case Bytecodes.GETFIELD:
187             case Bytecodes.PUTFIELD:
188                 FieldRef f = get(FieldRef.class, index);
189                 return get(ClassRef.class, f.classIndex).resolve(this);
190             case Bytecodes.INVOKEVIRTUAL:
191             case Bytecodes.INVOKESPECIAL:
192             case Bytecodes.INVOKESTATIC:
193             case Bytecodes.INVOKEINTERFACE:
194                 ExecutableRef e = get(ExecutableRef.class, index);
195                 return get(ClassRef.class, e.classIndex).resolve(this);
196             default:
197                 throw GraalError.shouldNotReachHere("Unexpected opcode: " + opcode);
198         }
199     }
200 
201     @Override
lookupUtf8(int index)202     public String lookupUtf8(int index) {
203         return ((Utf8) entries[index]).value;
204     }
205 
206     @Override
lookupSignature(int index)207     public Signature lookupSignature(int index) {
208         throw GraalError.shouldNotReachHere();
209     }
210 
211     @Override
lookupConstant(int index)212     public Object lookupConstant(int index) {
213         ClassfileConstant c = entries[index];
214         if (c instanceof Primitive) {
215             Primitive p = (Primitive) c;
216             return p.value;
217         }
218         switch (c.tag) {
219             case CONSTANT_Class:
220                 final int opcode = -1;
221                 return lookupType(index, opcode);
222             case ClassfileConstant.CONSTANT_String:
223                 return ((ClassfileConstant.StringRef) c).getValue(this);
224             default:
225                 throw new GraalError("Unexpected constant pool tag %s", c.tag);
226         }
227     }
228 
229     @Override
lookupAppendix(int index, int opcode)230     public JavaConstant lookupAppendix(int index, int opcode) {
231         if (opcode == Bytecodes.INVOKEVIRTUAL) {
232             return null;
233         }
234         throw GraalError.shouldNotReachHere();
235     }
236 }
237