|
|
|
/*
|
|
|
|
* 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.lazy;
|
|
|
|
|
|
|
|
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
|
|
|
|
import org.jetbrains.java.decompiler.struct.StructMethod;
|
|
|
|
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
|
|
|
|
import org.jetbrains.java.decompiler.struct.consts.ConstantPool;
|
|
|
|
import org.jetbrains.java.decompiler.util.DataInputFullStream;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
public class LazyLoader {
|
|
|
|
|
|
|
|
private final Map<String, Link> mapClassLinks = new HashMap<String, Link>();
|
|
|
|
private final IBytecodeProvider provider;
|
|
|
|
|
|
|
|
public LazyLoader(IBytecodeProvider provider) {
|
|
|
|
this.provider = provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addClassLink(String classname, Link link) {
|
|
|
|
mapClassLinks.put(classname, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeClassLink(String classname) {
|
|
|
|
mapClassLinks.remove(classname);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Link getClassLink(String classname) {
|
|
|
|
return mapClassLinks.get(classname);
|
|
|
|
}
|
|
|
|
|
|
|
|
public ConstantPool loadPool(String classname) {
|
|
|
|
try {
|
|
|
|
DataInputFullStream in = getClassStream(classname);
|
|
|
|
if (in == null) return null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
in.discard(8);
|
|
|
|
return new ConstantPool(in);
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
in.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (IOException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public byte[] loadBytecode(StructMethod mt, int codeFullLength) {
|
|
|
|
String className = mt.getClassStruct().qualifiedName;
|
|
|
|
|
|
|
|
try {
|
|
|
|
DataInputFullStream in = getClassStream(className);
|
|
|
|
if (in == null) return null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
in.discard(8);
|
|
|
|
|
|
|
|
ConstantPool pool = mt.getClassStruct().getPool();
|
|
|
|
if (pool == null) {
|
|
|
|
pool = new ConstantPool(in);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ConstantPool.skipPool(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
in.discard(6);
|
|
|
|
|
|
|
|
// interfaces
|
|
|
|
in.discard(in.readUnsignedShort() * 2);
|
|
|
|
|
|
|
|
// fields
|
|
|
|
int size = in.readUnsignedShort();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
in.discard(6);
|
|
|
|
skipAttributes(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
// methods
|
|
|
|
size = in.readUnsignedShort();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
in.discard(2);
|
|
|
|
|
|
|
|
int nameIndex = in.readUnsignedShort();
|
|
|
|
int descriptorIndex = in.readUnsignedShort();
|
|
|
|
|
|
|
|
String[] values = pool.getClassElement(ConstantPool.METHOD, className, nameIndex, descriptorIndex);
|
|
|
|
if (!mt.getName().equals(values[0]) || !mt.getDescriptor().equals(values[1])) {
|
|
|
|
skipAttributes(in);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int attrSize = in.readUnsignedShort();
|
|
|
|
for (int j = 0; j < attrSize; j++) {
|
|
|
|
int attrNameIndex = in.readUnsignedShort();
|
|
|
|
String attrName = pool.getPrimitiveConstant(attrNameIndex).getString();
|
|
|
|
if (!StructGeneralAttribute.ATTRIBUTE_CODE.equals(attrName)) {
|
|
|
|
in.discard(in.readInt());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
in.discard(12);
|
|
|
|
byte[] code = new byte[codeFullLength];
|
|
|
|
in.readFull(code);
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
in.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
catch (IOException ex) {
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public DataInputFullStream getClassStream(String externalPath, String internalPath) throws IOException {
|
|
|
|
byte[] bytes = provider.getBytecode(externalPath, internalPath);
|
|
|
|
return new DataInputFullStream(bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException {
|
|
|
|
Link link = mapClassLinks.get(qualifiedClassName);
|
|
|
|
return link == null ? null : getClassStream(link.externalPath, link.internalPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void skipAttributes(DataInputFullStream in) throws IOException {
|
|
|
|
int length = in.readUnsignedShort();
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
in.discard(2);
|
|
|
|
in.discard(in.readInt());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class Link {
|
|
|
|
public static final int CLASS = 1;
|
|
|
|
public static final int ENTRY = 2;
|
|
|
|
|
|
|
|
public int type;
|
|
|
|
public String externalPath;
|
|
|
|
public String internalPath;
|
|
|
|
|
|
|
|
public Link(int type, String externalPath, String internalPath) {
|
|
|
|
this.type = type;
|
|
|
|
this.externalPath = externalPath;
|
|
|
|
this.internalPath = internalPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|