blob: d534e6df6d3cde9d250d2d89bdd83e8ebaee7448 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the 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.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.VarType;
import org.jetbrains.java.decompiler.util.DataInputFullStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ConstantPool {
public static final int FIELD = 1;
public static final int METHOD = 2;
private List<PooledConstant> pool = new ArrayList<PooledConstant>();
private PoolInterceptor interceptor;
public ConstantPool(DataInputStream in) throws IOException {
int size = in.readUnsignedShort();
int[] pass = new int[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()));
break;
case CodeConstants.CONSTANT_Integer:
pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, new Integer(in.readInt())));
break;
case CodeConstants.CONSTANT_Float:
pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, new Float(in.readFloat())));
break;
case CodeConstants.CONSTANT_Long:
pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, new Long(in.readLong())));
pool.add(null);
i++;
break;
case CodeConstants.CONSTANT_Double:
pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, new Double(in.readDouble())));
pool.add(null);
i++;
break;
case CodeConstants.CONSTANT_Class:
case CodeConstants.CONSTANT_String:
case CodeConstants.CONSTANT_MethodType:
pool.add(new PrimitiveConstant(tag, in.readUnsignedShort()));
pass[i] = 1;
break;
case CodeConstants.CONSTANT_Fieldref:
case CodeConstants.CONSTANT_Methodref:
case CodeConstants.CONSTANT_InterfaceMethodref:
case CodeConstants.CONSTANT_NameAndType:
case CodeConstants.CONSTANT_InvokeDynamic:
pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort()));
if (tag == CodeConstants.CONSTANT_NameAndType) {
pass[i] = 1;
}
else {
pass[i] = 2;
}
break;
case CodeConstants.CONSTANT_MethodHandle:
pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort()));
pass[i] = 3;
break;
}
}
// resolving complex pool elements
for (int passIndex = 1; passIndex <= 3; passIndex++) {
for (int i = 1; i < size; i++) {
if (pass[i] == passIndex) {
pool.get(i).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++) {
switch (in.readUnsignedByte()) {
case CodeConstants.CONSTANT_Utf8:
in.readUTF();
break;
case CodeConstants.CONSTANT_Integer:
case CodeConstants.CONSTANT_Float:
case CodeConstants.CONSTANT_Fieldref:
case CodeConstants.CONSTANT_Methodref:
case CodeConstants.CONSTANT_InterfaceMethodref:
case CodeConstants.CONSTANT_NameAndType:
case CodeConstants.CONSTANT_InvokeDynamic:
in.discard(4);
break;
case CodeConstants.CONSTANT_Long:
case CodeConstants.CONSTANT_Double:
in.discard(8);
i++;
break;
case CodeConstants.CONSTANT_Class:
case CodeConstants.CONSTANT_String:
case CodeConstants.CONSTANT_MethodType:
in.discard(2);
break;
case CodeConstants.CONSTANT_MethodHandle:
in.discard(3);
}
}
}
public int size() {
return pool.size();
}
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 newElement = interceptor.getName(className + " " + elementName + " " + descriptor);
if (newElement != null) {
elementName = newElement.split(" ")[1];
}
int type = elementType == FIELD ? CodeConstants.CONSTANT_Fieldref : CodeConstants.CONSTANT_Methodref;
String newDescriptor = buildNewDescriptor(type, 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, ln.descriptor);
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;
}
private String buildNewClassname(String className) {
VarType vt = new VarType(className, true);
String newName = interceptor.getName(vt.value);
if (newName != null) {
StringBuilder buffer = new StringBuilder();
if (vt.arraydim > 0) {
for (int i = 0; i < vt.arraydim; i++) {
buffer.append("[");
}
buffer.append("L").append(newName).append(";");
}
else {
buffer.append(newName);
}
return buffer.toString();
}
return null;
}
private String buildNewDescriptor(int type, String descriptor) {
boolean updated = false;
if (type == CodeConstants.CONSTANT_Fieldref) {
FieldDescriptor fd = FieldDescriptor.parseDescriptor(descriptor);
VarType fType = fd.type;
if (fType.type == CodeConstants.TYPE_OBJECT) {
String newClassName = buildNewClassname(fType.value);
if (newClassName != null) {
fType.value = newClassName;
updated = true;
}
}
if (updated) {
return fd.getDescriptor();
}
}
else {
MethodDescriptor md = MethodDescriptor.parseDescriptor(descriptor);
// parameters
for (VarType paramType : md.params) {
if (paramType.type == CodeConstants.TYPE_OBJECT) {
String newClassName = buildNewClassname(paramType.value);
if (newClassName != null) {
paramType.value = newClassName;
updated = true;
}
}
}
// return value
if (md.ret.type == CodeConstants.TYPE_OBJECT) {
String newClassName = buildNewClassname(md.ret.value);
if (newClassName != null) {
md.ret.value = newClassName;
updated = true;
}
}
if (updated) {
return md.getDescriptor();
}
}
return null;
}
}