blob: 1c2751d840720fe8768afbc6386d01e186366abe [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 com.android.jack.tools.merger;
import com.android.jack.Jack;
import com.android.jack.dx.dex.DexFormat;
import com.android.jack.dx.dex.file.DexFile;
import com.android.jack.dx.io.DexBuffer;
import com.android.jack.dx.io.FieldId;
import com.android.jack.dx.io.MethodHandleId;
import com.android.jack.dx.io.MethodId;
import com.android.jack.dx.io.ProtoId;
import com.android.jack.dx.rop.cst.CstFieldRef;
import com.android.jack.dx.rop.cst.CstIndexMap;
import com.android.jack.dx.rop.cst.CstMethodHandleRef;
import com.android.jack.dx.rop.cst.CstMethodRef;
import com.android.jack.dx.rop.cst.CstPrototypeRef;
import com.android.jack.dx.rop.cst.CstString;
import com.android.jack.dx.rop.type.Prototype;
import com.android.jack.dx.rop.type.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* A tool to manage constants during dex merging.
*/
public class ConstantManager extends MergerTools {
@Nonnull
private final Map<String, CstString> string2CstStrings = new HashMap<String, CstString>();
@Nonnull
private final HashSet<CstPrototypeRef> cstPrototypeRefs = new HashSet<>();
@Nonnull
private final HashSet<CstFieldRef> cstFieldRefs = new HashSet<CstFieldRef>();
@Nonnull
private final HashSet<CstMethodRef> cstMethodRefs = new HashSet<CstMethodRef>();
@Nonnull
private final HashSet<Type> types = new HashSet<Type>();
@Nonnull
private final HashSet<CstMethodHandleRef> cstMethodHandleRefs = new HashSet<CstMethodHandleRef>();
@Nonnull
public Collection<CstString> getCstStrings() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(string2CstStrings.values());
}
@Nonnull
public Collection<CstFieldRef> getCstFieldRefs() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(cstFieldRefs);
}
@Nonnull
public Collection<CstMethodRef> getCstMethodRefs() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(cstMethodRefs);
}
@Nonnull
public Collection<Type> getTypes() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(types);
}
@Nonnull
public Collection<CstPrototypeRef> getCstPrototypeRefs() {
return Jack.getUnmodifiableCollections().getUnmodifiableCollection(cstPrototypeRefs);
}
@Nonnull
public CstIndexMap addDexFile(@Nonnull DexBuffer dexBuffer) throws MergingOverflowException {
CstIndexMap cstIndexMap = new CstIndexMap(dexBuffer);
List<String> cstStringsNewlyAdded = new ArrayList<String>();
List<CstPrototypeRef> cstPrototypeRefsNewlyAdded = new ArrayList<>();
List<CstFieldRef> cstFieldRefsNewlyAdded = new ArrayList<CstFieldRef>();
List<CstMethodRef> cstMethodRefsNewlyAdded = new ArrayList<CstMethodRef>();
List<Type> typesNewlyAdded = new ArrayList<Type>();
List<CstMethodHandleRef> cstMethodHandleRefsNewlyAdded = new ArrayList<CstMethodHandleRef>();
int idx = 0;
for (String string : dexBuffer.strings()) {
CstString cstString = string2CstStrings.get(string);
if (cstString == null) {
cstString = new CstString(string);
string2CstStrings.put(string, cstString);
cstStringsNewlyAdded.add(string);
}
cstIndexMap.addStringMapping(idx++, cstString);
}
idx = 0;
List<String> typeNames = dexBuffer.typeNames();
for (String typeNameDesc : typeNames) {
/*
* Note: VOID isn't put in the intern table of type, since it's special and shouldn't be found
* by a normal call to intern() from Type.
*/
Type type = null;
if (typeNameDesc.equals(Type.VOID.getDescriptor().getString())) {
type = Type.VOID;
} else {
type = Type.intern(typeNameDesc);
}
if (types.add(type)) {
typesNewlyAdded.add(type);
}
cstIndexMap.addTypeMapping(idx++, type);
}
idx = 0;
for (FieldId fieldId : dexBuffer.fieldIds()) {
CstFieldRef cstFieldRef =
new CstFieldRef(cstIndexMap.getType(fieldId.getDeclaringClassIndex()),
cstIndexMap.getCstString(fieldId.getNameIndex()),
cstIndexMap.getType(fieldId.getTypeIndex()));
if (cstFieldRefs.add(cstFieldRef)) {
cstFieldRefsNewlyAdded.add(cstFieldRef);
}
cstIndexMap.addFieldMapping(idx++, cstFieldRef);
}
idx = 0;
List<ProtoId> protoIds = dexBuffer.protoIds();
for (ProtoId protoId : protoIds) {
Prototype prototype = Prototype.intern(
cstIndexMap.getStdTypeList(dexBuffer.readTypeList(protoId.getParametersOffset())),
cstIndexMap.getType(protoId.getReturnTypeIndex()));
CstPrototypeRef cstProtoRef = new CstPrototypeRef(prototype);
if (cstPrototypeRefs.add(cstProtoRef)) {
cstPrototypeRefsNewlyAdded.add(cstProtoRef);
}
cstIndexMap.addPrototypeMapping(idx++, cstProtoRef);
}
idx = 0;
for (MethodId methodId : dexBuffer.methodIds()) {
int protoIdx = methodId.getProtoIndex();
CstMethodRef cstMethodRef =
new CstMethodRef(cstIndexMap.getType(methodId.getDeclaringClassIndex()),
cstIndexMap.getCstString(methodId.getNameIndex()),
cstIndexMap.getCstPrototype(protoIdx).getPrototype());
if (cstMethodRefs.add(cstMethodRef)) {
cstMethodRefsNewlyAdded.add(cstMethodRef);
}
cstIndexMap.addMethodMapping(idx++, cstMethodRef);
}
idx = 0;
for (MethodHandleId methodHandleId : dexBuffer.methodHandleIds()) {
CstMethodHandleRef cstMethodHandleRef = cstIndexMap.getCstMethodHandleRef(methodHandleId);
if (cstMethodHandleRefs.add(cstMethodHandleRef)) {
cstMethodHandleRefsNewlyAdded.add(cstMethodHandleRef);
}
cstIndexMap.addMethodHandleMapping(idx++, cstMethodHandleRef);
}
idx = 0;
for (Integer callSiteRefIdx : dexBuffer.callSiteIds()) {
cstIndexMap.addCallSiteMapping(idx++,
dexBuffer.readCstCallSiteRef(cstIndexMap, callSiteRefIdx.intValue()));
}
if ((cstMethodHandleRefs.size()) > DexFormat.MAX_MEMBER_IDX + 1) {
removeItems(cstStringsNewlyAdded, cstFieldRefsNewlyAdded, cstMethodRefsNewlyAdded,
typesNewlyAdded, cstPrototypeRefsNewlyAdded, cstMethodHandleRefsNewlyAdded);
throw new MethodHandleIdOverflowException();
}
if ((cstFieldRefs.size()) > DexFormat.MAX_MEMBER_IDX + 1) {
removeItems(cstStringsNewlyAdded, cstFieldRefsNewlyAdded, cstMethodRefsNewlyAdded,
typesNewlyAdded, cstPrototypeRefsNewlyAdded, cstMethodHandleRefsNewlyAdded);
throw new FieldIdOverflowException();
}
if ((cstMethodRefs.size()) > DexFormat.MAX_MEMBER_IDX + 1) {
removeItems(cstStringsNewlyAdded, cstFieldRefsNewlyAdded, cstMethodRefsNewlyAdded,
typesNewlyAdded, cstPrototypeRefsNewlyAdded, cstMethodHandleRefsNewlyAdded);
throw new MethodIdOverflowException();
}
if ((types.size()) > DexFormat.MAX_TYPE_IDX + 1) {
removeItems(cstStringsNewlyAdded, cstFieldRefsNewlyAdded, cstMethodRefsNewlyAdded,
typesNewlyAdded, cstPrototypeRefsNewlyAdded, cstMethodHandleRefsNewlyAdded);
throw new TypeIdOverflowException();
}
if ((cstPrototypeRefs.size()) > DexFormat.MAX_PROTOTYPE_IDX + 1) {
removeItems(cstStringsNewlyAdded, cstFieldRefsNewlyAdded, cstMethodRefsNewlyAdded,
typesNewlyAdded, cstPrototypeRefsNewlyAdded, cstMethodHandleRefsNewlyAdded);
throw new PrototypedOverflowException();
}
return cstIndexMap;
}
private void removeItems(@Nonnull List<String> cstStringsToRemove,
@Nonnull List<CstFieldRef> cstFieldRefsToRemove,
@Nonnull List<CstMethodRef> cstMethodRefsToRemove, @Nonnull List<Type> cstTypesToRemove,
@Nonnull List<CstPrototypeRef> cstPrototypeRefsToRemove,
@Nonnull List<CstMethodHandleRef> cstMethodHandleRefsToRemove) {
string2CstStrings.keySet().removeAll(cstStringsToRemove);
cstFieldRefs.removeAll(cstFieldRefsToRemove);
cstMethodRefs.removeAll(cstMethodRefsToRemove);
types.removeAll(cstTypesToRemove);
cstPrototypeRefs.removeAll(cstPrototypeRefsToRemove);
cstMethodHandleRefs.removeAll(cstMethodHandleRefsToRemove);
}
public boolean validate(@Nonnull DexFile dexFile) {
return ((dexFile.getStringIds().items().size() == string2CstStrings.size())
&& (dexFile.getFieldIds().items().size() == cstFieldRefs.size())
&& (dexFile.getMethodIds().items().size() == cstMethodRefs.size())
&& (dexFile.getTypeIds().items().size() == types.size()));
}
}