src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java (169 lines of code) (raw):

// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.java.decompiler.struct.consts; import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.ClassFormatException; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; import org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder; import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; import java.util.List; @SuppressWarnings("AssignmentToForLoopParameter") public class ConstantPool implements NewClassNameBuilder { public static final int FIELD = 1; public static final int METHOD = 2; private final List<PooledConstant> pool; private final PoolInterceptor interceptor; public ConstantPool(DataInputStream in) throws IOException { int size = in.readUnsignedShort(); pool = new ArrayList<>(size); BitSet[] nextPass = {new BitSet(size), new BitSet(size), new BitSet(size)}; // first dummy constant pool.add(null); // first pass: read the elements for (int i = 1; i < size; i++) { byte tag = (byte)in.readUnsignedByte(); switch (tag) { case CodeConstants.CONSTANT_Utf8 -> pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF())); case CodeConstants.CONSTANT_Integer -> pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, Integer.valueOf(in.readInt()))); case CodeConstants.CONSTANT_Float -> pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, in.readFloat())); case CodeConstants.CONSTANT_Long -> { pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, in.readLong())); pool.add(null); i++; } case CodeConstants.CONSTANT_Double -> { pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, in.readDouble())); pool.add(null); i++; } case CodeConstants.CONSTANT_Class, CodeConstants.CONSTANT_String, CodeConstants.CONSTANT_MethodType, CodeConstants.CONSTANT_Module, CodeConstants.CONSTANT_Package -> { pool.add(new PrimitiveConstant(tag, in.readUnsignedShort())); nextPass[0].set(i); } case CodeConstants.CONSTANT_NameAndType -> { pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); nextPass[0].set(i); } case CodeConstants.CONSTANT_Fieldref, CodeConstants.CONSTANT_Methodref, CodeConstants.CONSTANT_InterfaceMethodref, CodeConstants.CONSTANT_Dynamic, CodeConstants.CONSTANT_InvokeDynamic -> { pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); nextPass[1].set(i); } case CodeConstants.CONSTANT_MethodHandle -> { pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); nextPass[2].set(i); } default -> // Fail-fast on unknown constant pool entry. // We have no chance to process this class correctly. throw new ClassFormatException( String.format("Unsupported constant pool entry type %d at index #%d! ", Byte.toUnsignedInt(tag), i)); } } // resolving complex pool elements for (BitSet pass : nextPass) { int idx = 0; while ((idx = pass.nextSetBit(idx + 1)) > 0) { pool.get(idx).resolveConstant(this); } } // get global constant pool interceptor instance, if any available interceptor = DecompilerContext.getPoolInterceptor(); } public static void skipPool(DataInputFullStream in) throws IOException { int size = in.readUnsignedShort(); for (int i = 1; i < size; i++) { byte tag = (byte)in.readUnsignedByte(); switch (tag) { case CodeConstants.CONSTANT_Utf8 -> in.readUTF(); case CodeConstants.CONSTANT_Integer, CodeConstants.CONSTANT_Float, CodeConstants.CONSTANT_Fieldref, CodeConstants.CONSTANT_Methodref, CodeConstants.CONSTANT_InterfaceMethodref, CodeConstants.CONSTANT_NameAndType, CodeConstants.CONSTANT_Dynamic, CodeConstants.CONSTANT_InvokeDynamic -> in.discard(4); case CodeConstants.CONSTANT_Long, CodeConstants.CONSTANT_Double -> { in.discard(8); i++; } case CodeConstants.CONSTANT_Class, CodeConstants.CONSTANT_String, CodeConstants.CONSTANT_MethodType, CodeConstants.CONSTANT_Module, CodeConstants.CONSTANT_Package -> in.discard(2); case CodeConstants.CONSTANT_MethodHandle -> in.discard(3); default -> throw new RuntimeException("Invalid Constant Pool entry #" + i + " Type: " + tag); } } } public String[] getClassElement(int elementType, String className, int nameIndex, int descriptorIndex) { String elementName = ((PrimitiveConstant)getConstant(nameIndex)).getString(); String descriptor = ((PrimitiveConstant)getConstant(descriptorIndex)).getString(); if (interceptor != null) { String oldClassName = interceptor.getOldName(className); if (oldClassName != null) { className = oldClassName; } String newElement = interceptor.getName(className + ' ' + elementName + ' ' + descriptor); if (newElement != null) { elementName = newElement.split(" ")[1]; } String newDescriptor = buildNewDescriptor(elementType == FIELD, descriptor); if (newDescriptor != null) { descriptor = newDescriptor; } } return new String[]{elementName, descriptor}; } public PooledConstant getConstant(int index) { return pool.get(index); } public PrimitiveConstant getPrimitiveConstant(int index) { PrimitiveConstant cn = (PrimitiveConstant)getConstant(index); if (cn != null && interceptor != null) { if (cn.type == CodeConstants.CONSTANT_Class) { String newName = buildNewClassname(cn.getString()); if (newName != null) { cn = new PrimitiveConstant(CodeConstants.CONSTANT_Class, newName); } } } return cn; } public LinkConstant getLinkConstant(int index) { LinkConstant ln = (LinkConstant)getConstant(index); if (ln != null && interceptor != null && (ln.type == CodeConstants.CONSTANT_Fieldref || ln.type == CodeConstants.CONSTANT_Methodref || ln.type == CodeConstants.CONSTANT_InterfaceMethodref)) { String newClassName = buildNewClassname(ln.className); String newElement = interceptor.getName(ln.className + ' ' + ln.elementName + ' ' + ln.descriptor); String newDescriptor = buildNewDescriptor(ln.type == CodeConstants.CONSTANT_Fieldref, ln.descriptor); //TODO: Fix newElement being null caused by ln.classname being a leaf class instead of the class that declared the field/method. //See the comments of IDEA-137253 for more information. if (newClassName != null || newElement != null || newDescriptor != null) { String className = newClassName == null ? ln.className : newClassName; String elementName = newElement == null ? ln.elementName : newElement.split(" ")[1]; String descriptor = newDescriptor == null ? ln.descriptor : newDescriptor; ln = new LinkConstant(ln.type, className, elementName, descriptor); } } return ln; } @Override public String buildNewClassname(String className) { VarType vt = new VarType(className, true); String newName = interceptor.getName(vt.getValue()); if (newName != null) { StringBuilder buffer = new StringBuilder(); if (vt.getArrayDim() > 0) { buffer.append("[".repeat(vt.getArrayDim())).append('L').append(newName).append(';'); } else { buffer.append(newName); } return buffer.toString(); } return null; } private String buildNewDescriptor(boolean isField, String descriptor) { if (isField) { return FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } else { return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this); } } }