package org.graalvm.compiler.replacements.classfile;
import static org.graalvm.compiler.replacements.classfile.Classfile.skipFully;
import static org.graalvm.compiler.replacements.classfile.ClassfileConstant.CONSTANT_Class;
import java.io.DataInputStream;
import java.io.IOException;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.replacements.classfile.ClassfileConstant.ClassRef;
import org.graalvm.compiler.replacements.classfile.ClassfileConstant.ExecutableRef;
import org.graalvm.compiler.replacements.classfile.ClassfileConstant.FieldRef;
import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Primitive;
import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Utf8;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaField;
import jdk.vm.ci.meta.JavaMethod;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.Signature;
class ClassfileConstantPool implements ConstantPool {
final ClassfileConstant[] entries;
final ClassfileBytecodeProvider context;
public static class Bytecodes {
public static final int GETSTATIC = 178;
public static final int PUTSTATIC = 179;
public static final int GETFIELD = 180;
public static final int PUTFIELD = 181;
public static final int INVOKEVIRTUAL = 182;
public static final int INVOKESPECIAL = 183;
public static final int INVOKESTATIC = 184;
public static final int INVOKEINTERFACE = 185;
public static final int INVOKEDYNAMIC = 186;
}
ClassfileConstantPool(DataInputStream stream, ClassfileBytecodeProvider context) throws IOException {
this.context = context;
byte tag;
int count = stream.readUnsignedShort();
entries = new ClassfileConstant[count];
int i = 1;
while (i < count) {
entries[i] = readConstant(stream);
tag = entries[i].tag;
if ((tag == ClassfileConstant.CONSTANT_Double) || (tag == ClassfileConstant.CONSTANT_Long)) {
i += 2;
} else {
i += 1;
}
}
}
static final ClassfileConstant readConstant(DataInputStream stream) throws IOException {
byte tag = stream.readByte();
switch (tag) {
case ClassfileConstant.CONSTANT_Class:
return new ClassfileConstant.ClassRef(stream);
case ClassfileConstant.CONSTANT_Fieldref:
return new ClassfileConstant.FieldRef(stream);
case ClassfileConstant.CONSTANT_Methodref:
return new ClassfileConstant.MethodRef(stream);
case ClassfileConstant.CONSTANT_InterfaceMethodref:
return new ClassfileConstant.InterfaceMethodRef(stream);
case ClassfileConstant.CONSTANT_String:
return new ClassfileConstant.StringRef(stream);
case ClassfileConstant.CONSTANT_Integer:
return new ClassfileConstant.Primitive(tag, JavaConstant.forInt(stream.readInt()));
case ClassfileConstant.CONSTANT_Float:
return new ClassfileConstant.Primitive(tag, JavaConstant.forFloat(stream.readFloat()));
case ClassfileConstant.CONSTANT_Long:
return new ClassfileConstant.Primitive(tag, JavaConstant.forLong(stream.readLong()));
case ClassfileConstant.CONSTANT_Double:
return new ClassfileConstant.Primitive(tag, JavaConstant.forDouble(stream.readDouble()));
case ClassfileConstant.CONSTANT_NameAndType:
return new ClassfileConstant.NameAndType(stream);
case ClassfileConstant.CONSTANT_Utf8:
return new ClassfileConstant.Utf8(stream.readUTF());
case ClassfileConstant.CONSTANT_MethodHandle:
skipFully(stream, 3);
return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodHandle_info");
case ClassfileConstant.CONSTANT_MethodType:
skipFully(stream, 2);
return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodType_info");
case ClassfileConstant.CONSTANT_InvokeDynamic:
skipFully(stream, 4);
return new ClassfileConstant.Unsupported(tag, "CONSTANT_InvokeDynamic_info");
default:
throw new GraalError("Invalid constant pool tag: " + tag);
}
}
@Override
public int length() {
return entries.length;
}
<T extends ClassfileConstant> T get(Class<T> c, int index) {
return c.cast(entries[index]);
}
@Override
public void loadReferencedType(int index, int opcode) {
if (opcode == Bytecodes.INVOKEDYNAMIC) {
throw new GraalError("INVOKEDYNAMIC not supported by " + ClassfileBytecodeProvider.class.getSimpleName());
}
entries[index].loadReferencedType(this, index, opcode);
}
@Override
public JavaField lookupField(int index, ResolvedJavaMethod method, int opcode) {
return get(FieldRef.class, index).resolve(this, opcode);
}
@Override
public JavaMethod lookupMethod(int index, int opcode) {
if (opcode == Bytecodes.INVOKEDYNAMIC) {
throw new GraalError("INVOKEDYNAMIC not supported by" + ClassfileBytecodeProvider.class.getSimpleName());
}
return get(ExecutableRef.class, index).resolve(this, opcode);
}
@Override
public JavaType lookupType(int index, int opcode) {
return get(ClassRef.class, index).resolve(this);
}
@Override
public String lookupUtf8(int index) {
return ((Utf8) entries[index]).value;
}
@Override
public Signature lookupSignature(int index) {
throw GraalError.shouldNotReachHere();
}
@Override
public Object lookupConstant(int index) {
ClassfileConstant c = entries[index];
if (c instanceof Primitive) {
Primitive p = (Primitive) c;
return p.value;
}
switch (c.tag) {
case CONSTANT_Class:
final int opcode = -1;
return lookupType(index, opcode);
case ClassfileConstant.CONSTANT_String:
return ((ClassfileConstant.StringRef) c).getValue(this);
default:
throw new GraalError("Unexpected constant pool tag %s", c.tag);
}
}
@Override
public JavaConstant lookupAppendix(int index, int opcode) {
if (opcode == Bytecodes.INVOKEVIRTUAL) {
return null;
}
throw GraalError.shouldNotReachHere();
}
}