com.android.dx.dex.code.SwitchData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of builder Show documentation
Show all versions of builder Show documentation
Library to build Android applications.
/*
* Copyright (C) 2007 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.dx.dex.code;
import com.android.dx.io.Opcodes;
import com.android.dx.rop.code.RegisterSpecList;
import com.android.dx.rop.code.SourcePosition;
import com.android.dx.util.AnnotatedOutput;
import com.android.dx.util.Hex;
import com.android.dx.util.IntList;
/**
* Pseudo-instruction which holds switch data. The switch data is
* a map of values to target addresses, and this class writes the data
* in either a "packed" or "sparse" form.
*/
public final class SwitchData extends VariableSizeInsn {
/**
* {@code non-null;} address representing the instruction that uses this
* instance
*/
private final CodeAddress user;
/** {@code non-null;} sorted list of switch cases (keys) */
private final IntList cases;
/**
* {@code non-null;} corresponding list of code addresses; the branch
* target for each case
*/
private final CodeAddress[] targets;
/** whether the output table will be packed (vs. sparse) */
private final boolean packed;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param user {@code non-null;} address representing the instruction that
* uses this instance
* @param cases {@code non-null;} sorted list of switch cases (keys)
* @param targets {@code non-null;} corresponding list of code addresses; the
* branch target for each case
*/
public SwitchData(SourcePosition position, CodeAddress user,
IntList cases, CodeAddress[] targets) {
super(position, RegisterSpecList.EMPTY);
if (user == null) {
throw new NullPointerException("user == null");
}
if (cases == null) {
throw new NullPointerException("cases == null");
}
if (targets == null) {
throw new NullPointerException("targets == null");
}
int sz = cases.size();
if (sz != targets.length) {
throw new IllegalArgumentException("cases / targets mismatch");
}
if (sz > 65535) {
throw new IllegalArgumentException("too many cases");
}
this.user = user;
this.cases = cases;
this.targets = targets;
this.packed = shouldPack(cases);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return packed ? (int) packedCodeSize(cases) :
(int) sparseCodeSize(cases);
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out) {
int baseAddress = user.getAddress();
int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
int sz = targets.length;
if (packed) {
int firstCase = (sz == 0) ? 0 : cases.get(0);
int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
int outSz = lastCase - firstCase + 1;
out.writeShort(Opcodes.PACKED_SWITCH_PAYLOAD);
out.writeShort(outSz);
out.writeInt(firstCase);
int caseAt = 0;
for (int i = 0; i < outSz; i++) {
int outCase = firstCase + i;
int oneCase = cases.get(caseAt);
int relTarget;
if (oneCase > outCase) {
relTarget = defaultTarget;
} else {
relTarget = targets[caseAt].getAddress() - baseAddress;
caseAt++;
}
out.writeInt(relTarget);
}
} else {
out.writeShort(Opcodes.SPARSE_SWITCH_PAYLOAD);
out.writeShort(sz);
for (int i = 0; i < sz; i++) {
out.writeInt(cases.get(i));
}
for (int i = 0; i < sz; i++) {
int relTarget = targets[i].getAddress() - baseAddress;
out.writeInt(relTarget);
}
}
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new SwitchData(getPosition(), user, cases, targets);
}
/**
* Returns whether or not this instance's data will be output as packed.
*
* @return {@code true} iff the data is to be packed
*/
public boolean isPacked() {
return packed;
}
/** {@inheritDoc} */
@Override
protected String argString() {
StringBuffer sb = new StringBuffer(100);
int sz = targets.length;
for (int i = 0; i < sz; i++) {
sb.append("\n ");
sb.append(cases.get(i));
sb.append(": ");
sb.append(targets[i]);
}
return sb.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
int baseAddress = user.getAddress();
StringBuffer sb = new StringBuffer(100);
int sz = targets.length;
sb.append(packed ? "packed" : "sparse");
sb.append("-switch-payload // for switch @ ");
sb.append(Hex.u2(baseAddress));
for (int i = 0; i < sz; i++) {
int absTarget = targets[i].getAddress();
int relTarget = absTarget - baseAddress;
sb.append("\n ");
sb.append(cases.get(i));
sb.append(": ");
sb.append(Hex.u4(absTarget));
sb.append(" // ");
sb.append(Hex.s4(relTarget));
}
return sb.toString();
}
/**
* Gets the size of a packed table for the given cases, in 16-bit code
* units.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code >= -1;} the packed table size or {@code -1} if the
* cases couldn't possibly be represented as a packed table
*/
private static long packedCodeSize(IntList cases) {
int sz = cases.size();
long low = cases.get(0);
long high = cases.get(sz - 1);
long result = ((high - low + 1)) * 2 + 4;
return (result <= 0x7fffffff) ? result : -1;
}
/**
* Gets the size of a sparse table for the given cases, in 16-bit code
* units.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code > 0;} the sparse table size
*/
private static long sparseCodeSize(IntList cases) {
int sz = cases.size();
return (sz * 4L) + 2;
}
/**
* Determines whether the given list of cases warrant being packed.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code true} iff the table encoding the cases
* should be packed
*/
private static boolean shouldPack(IntList cases) {
int sz = cases.size();
if (sz < 2) {
return true;
}
long packedSize = packedCodeSize(cases);
long sparseSize = sparseCodeSize(cases);
/*
* We pick the packed representation if it is possible and
* would be as small or smaller than 5/4 of the sparse
* representation. That is, we accept some size overhead on
* the packed representation, since that format is faster to
* execute at runtime.
*/
return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy