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