ClassfileConstant.java revision 12968:4d8a004e5c6d
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 */
23package org.graalvm.compiler.replacements.classfile;
24
25import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD;
26import static org.graalvm.compiler.bytecode.Bytecodes.GETSTATIC;
27import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD;
28import static org.graalvm.compiler.bytecode.Bytecodes.PUTSTATIC;
29
30import java.io.DataInputStream;
31import java.io.IOException;
32
33import org.graalvm.compiler.bytecode.Bytecodes;
34import org.graalvm.compiler.debug.GraalError;
35
36import jdk.vm.ci.meta.JavaConstant;
37import jdk.vm.ci.meta.ResolvedJavaField;
38import jdk.vm.ci.meta.ResolvedJavaMethod;
39import jdk.vm.ci.meta.ResolvedJavaType;
40
41abstract 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(cp.context, 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(cp.context, 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(cp.context, 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(cp.context, 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(ClassfileBytecodeProvider context, ResolvedJavaType c, String name, String descriptor, boolean isStatic) {
277        ResolvedJavaMethod method = context.findMethod(c, name, descriptor, isStatic);
278        if (method != null) {
279            return method;
280        }
281        if (!c.isJavaLangObject() && !c.isInterface()) {
282            method = resolveMethod(context, c.getSuperclass(), name, descriptor, isStatic);
283            if (method != null) {
284                return method;
285            }
286        }
287        for (ResolvedJavaType i : c.getInterfaces()) {
288            method = resolveMethod(context, i, name, descriptor, isStatic);
289            if (method != null) {
290                return method;
291            }
292        }
293        return null;
294    }
295
296    static ResolvedJavaField resolveField(ClassfileBytecodeProvider context, ResolvedJavaType c, String name, String fieldType, boolean isStatic) {
297        ResolvedJavaField field = context.findField(c, name, fieldType, isStatic);
298        if (field != null) {
299            return field;
300        }
301        if (!c.isJavaLangObject() && !c.isInterface()) {
302            field = resolveField(context, c.getSuperclass(), name, fieldType, isStatic);
303            if (field != null) {
304                return field;
305            }
306        }
307        for (ResolvedJavaType i : c.getInterfaces()) {
308            field = resolveField(context, i, name, fieldType, isStatic);
309            if (field != null) {
310                return field;
311            }
312        }
313        return null;
314    }
315}
316