Lots of misc cleanup/refactoring to work with the new dexlib

git-svn-id: https://smali.googlecode.com/svn/trunk@336 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2009-08-03 05:47:16 +00:00
parent 613d7e6f69
commit cf6729c1ee
24 changed files with 387 additions and 268 deletions

View File

@ -38,9 +38,9 @@ public class ClassDefinition {
private ClassDefItem classDefItem;
private ClassDataItem classDataItem;
private HashMap<Integer, AnnotationSetItem> methodAnnotations = new HashMap<Integer, AnnotationSetItem>();
private HashMap<Integer, AnnotationSetItem> fieldAnnotations = new HashMap<Integer, AnnotationSetItem>();
private HashMap<Integer, AnnotationSetRefList> parameterAnnotations = new HashMap<Integer, AnnotationSetRefList>();
private HashMap<Integer, AnnotationSetItem> methodAnnotationsMap = new HashMap<Integer, AnnotationSetItem>();
private HashMap<Integer, AnnotationSetItem> fieldAnnotationsMap = new HashMap<Integer, AnnotationSetItem>();
private HashMap<Integer, AnnotationSetRefList> parameterAnnotationsMap = new HashMap<Integer, AnnotationSetRefList>();
public ClassDefinition(ClassDefItem classDefItem) {
this.classDefItem = classDefItem;
@ -49,32 +49,29 @@ public class ClassDefinition {
}
private void buildAnnotationMaps() {
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotationDirectory();
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
if (annotationDirectory == null) {
return;
}
List<AnnotationDirectoryItem.MethodAnnotation> methodAnnotationList = annotationDirectory.getMethodAnnotations();
if (methodAnnotationList != null) {
for (AnnotationDirectoryItem.MethodAnnotation methodAnnotation: methodAnnotationList) {
methodAnnotations.put(methodAnnotation.getMethod().getIndex(), methodAnnotation.getAnnotationSet());
annotationDirectory.iterateMethodAnnotations(new AnnotationDirectoryItem.MethodAnnotationIteratorDelegate() {
public void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations) {
methodAnnotationsMap.put(method.getIndex(), methodAnnotations);
}
}
});
List<AnnotationDirectoryItem.FieldAnnotation> fieldAnnotationList = annotationDirectory.getFieldAnnotations();
if (fieldAnnotationList != null) {
for (AnnotationDirectoryItem.FieldAnnotation fieldAnnotation: fieldAnnotationList) {
fieldAnnotations.put(fieldAnnotation.getField().getIndex(), fieldAnnotation.getAnnotationSet());
annotationDirectory.iterateFieldAnnotations(new AnnotationDirectoryItem.FieldAnnotationIteratorDelegate() {
public void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations) {
fieldAnnotationsMap.put(field.getIndex(), fieldAnnotations);
}
}
});
List<AnnotationDirectoryItem.ParameterAnnotation> parameterAnnotationList =
annotationDirectory.getParameterAnnotations();
if (parameterAnnotationList != null) {
for (AnnotationDirectoryItem.ParameterAnnotation parameterAnnotation: parameterAnnotationList) {
parameterAnnotations.put(parameterAnnotation.getMethod().getIndex(), parameterAnnotation.getParameterAnnotations());
annotationDirectory.iteratParameterAnnotations(
new AnnotationDirectoryItem.ParameterAnnotationIteratorDelegate() {
public void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations) {
parameterAnnotationsMap.put(method.getIndex(), parameterAnnotations);
}
}
});
}
public List<String> getAccessFlags() {
@ -100,16 +97,16 @@ public class ClassDefinition {
}
public String getSourceFile() {
return classDefItem.getSourceFile();
return classDefItem.getSourceFile().getStringValue();
}
public List<String> getInterfaces() {
List<String> interfaces = new ArrayList<String>();
List<TypeIdItem> interfaceList = classDefItem.getInterfaces();
TypeListItem interfaceList = classDefItem.getInterfaces();
if (interfaceList != null) {
for (TypeIdItem typeIdItem: interfaceList) {
for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
interfaces.add(typeIdItem.getTypeDescriptor());
}
}
@ -122,22 +119,22 @@ public class ClassDefinition {
if (classDataItem != null) {
EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticInitializers();
EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
List<EncodedValue> staticInitializers;
EncodedValue[] staticInitializers;
if (encodedStaticInitializers != null) {
staticInitializers = encodedStaticInitializers.getEncodedArray().getValues();
staticInitializers = encodedStaticInitializers.getEncodedArray().values;
} else {
staticInitializers = new ArrayList<EncodedValue>();
staticInitializers = new EncodedValue[0];
}
int i=0;
for (ClassDataItem.EncodedField field: classDataItem.getStaticFields()) {
EncodedValue encodedValue = null;
if (i < staticInitializers.size()) {
encodedValue = staticInitializers.get(i);
if (i < staticInitializers.length) {
encodedValue = staticInitializers[i];
}
AnnotationSetItem annotationSet = fieldAnnotations.get(field.getField().getIndex());
AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
staticFields.add(new FieldDefinition(field, encodedValue, annotationSet));
i++;
}
@ -150,7 +147,7 @@ public class ClassDefinition {
if (classDataItem != null) {
for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) {
AnnotationSetItem annotationSet = fieldAnnotations.get(field.getField().getIndex());
AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
instanceFields.add(new FieldDefinition(field, annotationSet));
}
}
@ -163,8 +160,8 @@ public class ClassDefinition {
if (classDataItem != null) {
for (ClassDataItem.EncodedMethod method: classDataItem.getDirectMethods()) {
AnnotationSetItem annotationSet = methodAnnotations.get(method.getMethod().getIndex());
AnnotationSetRefList parameterAnnotationList = parameterAnnotations.get(method.getMethod().getIndex());
AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
directMethods.add(new MethodDefinition(method, annotationSet, parameterAnnotationList));
}
}
@ -177,8 +174,8 @@ public class ClassDefinition {
if (classDataItem != null) {
for (ClassDataItem.EncodedMethod method: classDataItem.getVirtualMethods()) {
AnnotationSetItem annotationSet = methodAnnotations.get(method.getMethod().getIndex());
AnnotationSetRefList parameterAnnotationList = parameterAnnotations.get(method.getMethod().getIndex());
AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
virtualMethods.add(new MethodDefinition(method, annotationSet, parameterAnnotationList));
}
}
@ -187,7 +184,7 @@ public class ClassDefinition {
}
public List<AnnotationAdaptor> getAnnotations() {
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotationDirectory();
AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
if (annotationDirectory == null) {
return null;
}
@ -199,7 +196,7 @@ public class ClassDefinition {
List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>();
for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) {
for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
annotationAdaptors.add(new AnnotationAdaptor(annotationItem));
}
return annotationAdaptors;

View File

@ -29,8 +29,8 @@
package org.jf.baksmali.Adaptors;
public class DebugMethodItem extends MethodItem {
private String template;
private int sortOrder;
private final String template;
private final int sortOrder;
public DebugMethodItem(int offset, String template, int sortOrder) {
super(offset);

View File

@ -28,17 +28,18 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.dexlib.EncodedValue.AnnotationEncodedValueSubField;
import org.jf.dexlib.EncodedValue.AnnotationElement;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue;
import org.jf.dexlib.StringIdItem;
import org.jf.baksmali.Adaptors.Reference.TypeReference;
import java.util.List;
import java.util.ArrayList;
public class AnnotationEncodedValueAdaptor extends EncodedValueAdaptor {
private AnnotationEncodedValueSubField encodedAnnotation;
private AnnotationEncodedSubValue encodedAnnotation;
public AnnotationEncodedValueAdaptor(AnnotationEncodedValueSubField encodedAnnotation) {
public AnnotationEncodedValueAdaptor(AnnotationEncodedSubValue encodedAnnotation) {
this.encodedAnnotation = encodedAnnotation;
}
@ -51,32 +52,35 @@ public class AnnotationEncodedValueAdaptor extends EncodedValueAdaptor {
}
public TypeReference getAnnotationType() {
return new TypeReference(encodedAnnotation.getAnnotationType());
return new TypeReference(encodedAnnotation.annotationType);
}
public List<AnnotationElementAdaptor> getElements() {
List<AnnotationElementAdaptor> elements = new ArrayList<AnnotationElementAdaptor>();
for (AnnotationElement annotationElement: encodedAnnotation.getAnnotationElements()) {
elements.add(new AnnotationElementAdaptor(annotationElement));
for (int i=0; i<encodedAnnotation.names.length; i++) {
elements.add(new AnnotationElementAdaptor(encodedAnnotation.names[i], encodedAnnotation.values[i]));
}
return elements;
}
public static class AnnotationElementAdaptor {
private AnnotationElement annotationElement;
private StringIdItem name;
private EncodedValue value;
public AnnotationElementAdaptor(AnnotationElement annotationElement) {
this.annotationElement = annotationElement;
public AnnotationElementAdaptor(StringIdItem name, EncodedValue value) {
this.name = name;
this.value = value;
}
public String getName() {
return annotationElement.getName().getStringValue();
return name.getStringValue();
}
public EncodedValueAdaptor getValue() {
return EncodedValueAdaptor.make(annotationElement.getEncodedValue());
return EncodedValueAdaptor.make(value);
}
}
}

View File

@ -28,16 +28,16 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.dexlib.EncodedValue.ArrayEncodedValueSubField;
import org.jf.dexlib.EncodedValue.EncodedValue;
import org.jf.dexlib.EncodedValue.ArrayEncodedValue;
import java.util.List;
import java.util.ArrayList;
public class ArrayEncodedValueAdaptor extends EncodedValueAdaptor {
private ArrayEncodedValueSubField encodedArray;
private ArrayEncodedValue encodedArray;
public ArrayEncodedValueAdaptor(ArrayEncodedValueSubField encodedArray) {
public ArrayEncodedValueAdaptor(ArrayEncodedValue encodedArray) {
this.encodedArray = encodedArray;
}
@ -48,7 +48,7 @@ public class ArrayEncodedValueAdaptor extends EncodedValueAdaptor {
public Object getValue() {
List<EncodedValueAdaptor> encodedValues = new ArrayList<EncodedValueAdaptor>();
for (EncodedValue encodedValue: encodedArray.getValues()) {
for (EncodedValue encodedValue: encodedArray.values) {
encodedValues.add(EncodedValueAdaptor.make(encodedValue));
}
return encodedValues;

View File

@ -28,7 +28,6 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.dexlib.IndexedItem;
import org.jf.baksmali.Adaptors.Reference.Reference;
public class EncodedIndexedItemAdaptor extends EncodedValueAdaptor {

View File

@ -29,52 +29,43 @@
package org.jf.baksmali.Adaptors.EncodedValue;
import org.jf.dexlib.EncodedValue.*;
import org.jf.dexlib.StringIdItem;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.FieldIdItem;
import org.jf.baksmali.Adaptors.Reference.*;
public abstract class EncodedValueAdaptor {
public static EncodedValueAdaptor make(EncodedValue encodedValue) {
switch (encodedValue.getValueType()) {
case VALUE_ANNOTATION:
return new AnnotationEncodedValueAdaptor((AnnotationEncodedValueSubField)encodedValue.getValue());
return new AnnotationEncodedValueAdaptor((AnnotationEncodedValue)encodedValue);
case VALUE_ARRAY:
return new ArrayEncodedValueAdaptor((ArrayEncodedValueSubField)encodedValue.getValue());
return new ArrayEncodedValueAdaptor((ArrayEncodedValue)encodedValue);
case VALUE_BOOLEAN:
return new SimpleEncodedValueAdaptor(((BoolEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((BooleanEncodedValue)encodedValue).value);
case VALUE_BYTE:
return new SimpleEncodedValueAdaptor(((ByteEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((ByteEncodedValue)encodedValue).value);
case VALUE_CHAR:
return new SimpleEncodedValueAdaptor(((CharEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((CharEncodedValue)encodedValue).value);
case VALUE_DOUBLE:
return new SimpleEncodedValueAdaptor(((DoubleEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((DoubleEncodedValue)encodedValue).value);
case VALUE_ENUM:
EncodedIndexedItemReference enumEncodedReference = (EncodedIndexedItemReference)encodedValue.getValue();
return new EnumEncodedValueAdaptor(new FieldReference((FieldIdItem)enumEncodedReference.getValue()));
return new EnumEncodedValueAdaptor(new FieldReference(((EnumEncodedValue)encodedValue).value));
case VALUE_FIELD:
EncodedIndexedItemReference fieldEncodedReference = (EncodedIndexedItemReference)encodedValue.getValue();
return new EncodedIndexedItemAdaptor(new FieldReference((FieldIdItem)fieldEncodedReference.getValue()));
return new EncodedIndexedItemAdaptor(new FieldReference(((FieldEncodedValue)encodedValue).value));
case VALUE_FLOAT:
return new SimpleEncodedValueAdaptor(((FloatEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((FloatEncodedValue)encodedValue).value);
case VALUE_INT:
return new SimpleEncodedValueAdaptor(((IntEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((IntEncodedValue)encodedValue).value);
case VALUE_LONG:
return new SimpleEncodedValueAdaptor(((LongEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((LongEncodedValue)encodedValue).value);
case VALUE_METHOD:
EncodedIndexedItemReference methodEncodedReference = (EncodedIndexedItemReference)encodedValue.getValue();
return new EncodedIndexedItemAdaptor(new MethodReference((MethodIdItem)methodEncodedReference.getValue()));
return new EncodedIndexedItemAdaptor(new MethodReference(((MethodEncodedValue)encodedValue).value));
case VALUE_NULL:
return new SimpleEncodedValueAdaptor("null");
case VALUE_SHORT:
return new SimpleEncodedValueAdaptor(((ShortEncodedValueSubField)encodedValue.getValue()).getValue());
return new SimpleEncodedValueAdaptor(((ShortEncodedValue)encodedValue).value);
case VALUE_STRING:
EncodedIndexedItemReference stringEncodedReference = (EncodedIndexedItemReference)encodedValue.getValue();
return new EncodedIndexedItemAdaptor(new StringReference((StringIdItem)stringEncodedReference.getValue()));
return new EncodedIndexedItemAdaptor(new StringReference(((StringEncodedValue)encodedValue).value));
case VALUE_TYPE:
EncodedIndexedItemReference typeEncodedReference = (EncodedIndexedItemReference)encodedValue.getValue();
return new EncodedIndexedItemAdaptor(new TypeReference((TypeIdItem)typeEncodedReference.getValue()));
return new EncodedIndexedItemAdaptor(new TypeReference(((TypeEncodedValue)encodedValue).value));
}
return null;
}

View File

@ -52,7 +52,7 @@ public class FieldDefinition {
public FieldDefinition(ClassDataItem.EncodedField encodedField, EncodedValue initialValue,
AnnotationSetItem annotationSet) {
this.encodedField = encodedField;
this.fieldIdItem = encodedField.getField();
this.fieldIdItem = encodedField.field;
this.initialValue = initialValue;
this.annotationSet = annotationSet;
}
@ -62,7 +62,7 @@ public class FieldDefinition {
if (accessFlags == null) {
accessFlags = new ArrayList<String>();
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(encodedField.getAccessFlags())) {
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForField(encodedField.accessFlags)) {
accessFlags.add(accessFlag.toString());
}
}
@ -100,7 +100,7 @@ public class FieldDefinition {
List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>();
for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) {
for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
annotationAdaptors.add(new AnnotationAdaptor(annotationItem));
}
return annotationAdaptors;

View File

@ -29,9 +29,12 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction;
import org.jf.dexlib.Util.ByteArray;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.Arrays;
public class ArrayDataMethodItem extends InstructionFormatMethodItem<ArrayDataPseudoInstruction> {
public ArrayDataMethodItem(int offset, ArrayDataPseudoInstruction instruction) {
@ -42,24 +45,24 @@ public class ArrayDataMethodItem extends InstructionFormatMethodItem<ArrayDataPs
return instruction.getElementWidth();
}
public List<ByteArray> getValues() {
List<ByteArray> values = new ArrayList<ByteArray>();
public Iterator<byte[]> getValues() {
return new Iterator<byte[]>() {
int position;
final Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
for (byte[] byteArray: instruction.getValues()) {
values.add(new ByteArray(byteArray));
}
return values;
}
public boolean hasNext() {
return iterator.hasNext();
}
public static class ByteArray
{
byte[] byteArray;
public ByteArray(byte[] byteArray) {
this.byteArray = byteArray;
}
public byte[] next() {
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
byte[] array = new byte[element.elementWidth];
System.arraycopy(element.buffer, element.bufferIndex, array, 0, element.elementWidth);
return array;
}
public byte[] getByteArray() {
return byteArray;
}
public void remove() {
}
};
}
}

View File

@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.Instruction21c;
public class Instruction21cMethodItem extends InstructionFormatMethodItem<Instruction21c> {
public class Instruction21cMethodItem extends ReferenceInstructionFormatMethodItem<Instruction21c> {
public Instruction21cMethodItem(int offset, Instruction21c instruction) {
super(offset, instruction);
}

View File

@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.Instruction22c;
public class Instruction22cMethodItem extends InstructionFormatMethodItem<Instruction22c> {
public class Instruction22cMethodItem extends ReferenceInstructionFormatMethodItem<Instruction22c> {
public Instruction22cMethodItem(int offset, Instruction22c instruction) {
super(offset, instruction);
}

View File

@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.Instruction31c;
public class Instruction31cMethodItem extends InstructionFormatMethodItem<Instruction31c> {
public class Instruction31cMethodItem extends ReferenceInstructionFormatMethodItem<Instruction31c> {
public Instruction31cMethodItem(int offset, Instruction31c instruction) {
super(offset, instruction);
}

View File

@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.Instruction35c;
public class Instruction35cMethodItem extends InstructionFormatMethodItem<Instruction35c> {
public class Instruction35cMethodItem extends ReferenceInstructionFormatMethodItem<Instruction35c> {
public Instruction35cMethodItem(int offset, Instruction35c instruction) {
super(offset, instruction);
}

View File

@ -30,7 +30,7 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.Instruction3rc;
public class Instruction3rcMethodItem extends InstructionFormatMethodItem<Instruction3rc> {
public class Instruction3rcMethodItem extends ReferenceInstructionFormatMethodItem<Instruction3rc> {
public Instruction3rcMethodItem(int offset, Instruction3rc instruction) {
super(offset, instruction);
}

View File

@ -46,14 +46,10 @@ public abstract class InstructionFormatMethodItem<T extends Instruction> extends
}
public String getOpcode() {
return instruction.getOpcode().name;
return instruction.opcode.name;
}
public String getTemplate() {
return instruction.getFormat().name();
}
public Reference getReference() {
return Reference.makeReference(instruction.getReferencedItem());
}
}

View File

@ -29,6 +29,9 @@
package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction;
import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction;
import java.util.Iterator;
public class PackedSwitchMethodItem extends InstructionFormatMethodItem<PackedSwitchDataPseudoInstruction> {
private int baseAddress;
@ -42,14 +45,27 @@ public class PackedSwitchMethodItem extends InstructionFormatMethodItem<PackedSw
return instruction.getFirstKey();
}
public String[] getTargets() {
int[] targetValues = instruction.getTargets();
String[] targets = new String[targetValues.length];
private static class PackedSwitchTarget {
public String Target;
}
for (int i=0; i<targetValues.length; i++) {
targets[i] = Integer.toHexString(targetValues[i] + baseAddress);
}
public Iterator<PackedSwitchTarget> getTargets() {
return new Iterator<PackedSwitchTarget>() {
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = instruction.getTargets();
PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget();
return targets;
public boolean hasNext() {
return iterator.hasNext();
}
public PackedSwitchTarget next() {
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
packedSwitchTarget.Target = Integer.toHexString(target.target + baseAddress);
return packedSwitchTarget;
}
public void remove() {
}
};
}
}

View File

@ -0,0 +1,44 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.baksmali.Adaptors.Reference.Reference;
public abstract class ReferenceInstructionFormatMethodItem<T extends InstructionWithReference>
extends InstructionFormatMethodItem<T> {
public ReferenceInstructionFormatMethodItem(int offset, T instruction) {
super(offset, instruction);
}
public Reference getReference() {
return Reference.makeReference(instruction.getReferencedItem());
}
}

View File

@ -30,6 +30,8 @@ package org.jf.baksmali.Adaptors.Format;
import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction;
import java.util.Iterator;
public class SparseSwitchMethodItem extends InstructionFormatMethodItem<SparseSwitchDataPseudoInstruction> {
private int baseAddress;
@ -38,18 +40,29 @@ public class SparseSwitchMethodItem extends InstructionFormatMethodItem<SparseSw
this.baseAddress = baseAddress;
}
public int[] getKeys() {
return instruction.getKeys();
private static class SparseSwitchTarget {
public int Value;
public String Target;
}
public String[] getTargets() {
int[] targetValues = instruction.getTargets();
String[] targets = new String[targetValues.length];
public Iterator<SparseSwitchTarget> getTargets() {
return new Iterator<SparseSwitchTarget>() {
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = instruction.getTargets();
SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget();
for (int i=0; i<targetValues.length; i++) {
targets[i] = Integer.toHexString(targetValues[i] + baseAddress);
}
public boolean hasNext() {
return iterator.hasNext();
}
return targets;
public SparseSwitchTarget next() {
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
sparseSwitchTarget.Value = target.value;
sparseSwitchTarget.Target = Integer.toHexString(target.target + baseAddress);
return sparseSwitchTarget;
}
public void remove() {
}
};
}
}

View File

@ -28,40 +28,21 @@
package org.jf.baksmali.Adaptors;
import org.jf.dexlib.Util.DebugInfoDecoder;
import org.jf.dexlib.Util.Utf8Utils;
import org.jf.dexlib.TypeIdItem;
import org.jf.dexlib.StringIdItem;
public class LocalDebugMethodItem extends DebugMethodItem {
private DebugInfoDecoder.Local local;
public final int Register;
public final StringIdItem Name;
public final TypeIdItem Type;
public final StringIdItem Signature;
public LocalDebugMethodItem(int offset, String template, int sortOrder, DebugInfoDecoder.Local local) {
public LocalDebugMethodItem(int offset, String template, int sortOrder, int register, StringIdItem name,
TypeIdItem type, StringIdItem signature) {
super(offset, template, sortOrder);
this.local = local;
}
public int getRegister() {
return local.register;
}
public String getType() {
if (local.type == null) {
return null;
}
return local.type.getTypeDescriptor();
}
public String getName() {
if (local.name == null) {
return null;
}
return local.name.getStringValue();
}
public String getSignature() {
if (local.signature == null) {
return null;
}
return Utf8Utils.escapeString(local.signature.getStringValue());
this.Register = register;
this.Name = name;
this.Type = type;
this.Signature = signature;
}
}

View File

@ -30,12 +30,12 @@ package org.jf.baksmali.Adaptors;
import org.jf.baksmali.Adaptors.Format.*;
import org.jf.dexlib.*;
import org.jf.dexlib.Debug.DebugInstructionIterator;
import org.jf.dexlib.Code.Format.*;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.InstructionField;
import org.jf.dexlib.Code.Opcode;
import org.jf.dexlib.Code.InstructionIterator;
import org.jf.dexlib.Util.AccessFlags;
import org.jf.dexlib.Util.DebugInfoDecoder;
import java.util.*;
@ -49,8 +49,8 @@ public class MethodDefinition {
public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod, AnnotationSetItem annotationSet,
AnnotationSetRefList parameterAnnotations) {
this.encodedMethod = encodedMethod;
this.methodIdItem = encodedMethod.getMethod();
this.codeItem = encodedMethod.getCodeItem();
this.methodIdItem = encodedMethod.method;
this.codeItem = encodedMethod.codeItem;
this.annotationSet = annotationSet;
this.parameterAnnotations = parameterAnnotations;
}
@ -64,7 +64,7 @@ public class MethodDefinition {
if (accessFlags == null) {
accessFlags = new ArrayList<String>();
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.getAccessFlags())) {
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
accessFlags.add(accessFlag.toString());
}
}
@ -106,7 +106,7 @@ public class MethodDefinition {
List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>();
for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) {
for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
annotationAdaptors.add(new AnnotationAdaptor(annotationItem));
}
return annotationAdaptors;
@ -122,9 +122,9 @@ public class MethodDefinition {
List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>();
if (parameterAnnotations != null) {
List<AnnotationSetItem> _annotations = parameterAnnotations.getAnnotationSets();
AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets();
if (_annotations != null) {
annotations.addAll(_annotations);
annotations.addAll(Arrays.asList(_annotations));
}
parameterCount = annotations.size();
@ -132,9 +132,11 @@ public class MethodDefinition {
List<String> parameterNames = new ArrayList<String>();
if (debugInfoItem != null) {
List<String> _parameterNames = debugInfoItem.getParameterNames();
StringIdItem[] _parameterNames = debugInfoItem.getParameterNames();
if (_parameterNames != null) {
parameterNames.addAll(_parameterNames);
for (StringIdItem parameterName: _parameterNames) {
parameterNames.add(parameterName.getStringValue());
}
}
if (parameterCount < parameterNames.size()) {
@ -160,27 +162,7 @@ public class MethodDefinition {
return parameterAdaptors;
}
private List<List<AnnotationAdaptor>> getParameterAnnotations() {
if (parameterAnnotations == null) {
return null;
}
List<List<AnnotationAdaptor>> parameterAnnotationList = new ArrayList<List<AnnotationAdaptor>>();
List<AnnotationSetItem> annotationSets = parameterAnnotations.getAnnotationSets();
for (AnnotationSetItem annotationSet: annotationSets) {
List<AnnotationAdaptor> parameterAnnotationAdaptors = new ArrayList<AnnotationAdaptor>();
for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) {
parameterAnnotationAdaptors.add(new AnnotationAdaptor(annotationItem));
}
parameterAnnotationList.add(parameterAnnotationAdaptors);
}
return parameterAnnotationList;
}
public List<String> getParameterNames() {
public StringIdItem[] getParameterNames() {
if (codeItem == null) {
return null;
}
@ -189,8 +171,8 @@ public class MethodDefinition {
if (debugInfoItem == null) {
return null;
}
return debugInfoItem.getParameterNames();
return debugInfoItem.getParameterNames();
}
private List<MethodItem> methodItems = null;
@ -228,24 +210,44 @@ public class MethodDefinition {
return;
}
int offset = 0;
for (InstructionField instructionField: codeItem.getInstructions()) {
Instruction instruction = instructionField.getInstruction();
if (instruction.getOpcode() == Opcode.PACKED_SWITCH) {
packedSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset);
} else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) {
sparseSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset);
}
final byte[] encodedInstructions = codeItem.getEncodedInstructions();
offset += instructionField.getSize(offset * 2) / 2;
}
InstructionIterator.IterateInstructions(encodedInstructions,
new InstructionIterator.ProcessRawInstructionDelegate() {
public void ProcessNormalInstruction(Opcode opcode, int index) {
if (opcode == Opcode.PACKED_SWITCH) {
Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
methodIdItem.getDexFile(), opcode, encodedInstructions, index);
packedSwitchMap.put(ins.getOffset(), index/2);
} else if (opcode == Opcode.SPARSE_SWITCH) {
Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
methodIdItem.getDexFile(), opcode, encodedInstructions, index);
sparseSwitchMap.put(ins.getOffset(), index/2);
}
}
offset = 0;
for (InstructionField instructionField: codeItem.getInstructions()) {
addMethodItemsForInstruction(offset, instructionField);
blanks.add(new BlankMethodItem(offset));
offset += instructionField.getSize(offset*2) / 2;
}
public void ProcessReferenceInstruction(Opcode opcode, int index) {
}
public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
}
public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) {
}
});
InstructionIterator.IterateInstructions(methodIdItem.getDexFile(), encodedInstructions,
new InstructionIterator.ProcessInstructionDelegate() {
public void ProcessInstruction(int index, Instruction instruction) {
int offset = index/2;
addMethodItemsForInstruction(offset, instruction);
blanks.add(new BlankMethodItem(offset));
}
});
blanks.remove(blanks.size()-1);
addTries();
@ -253,9 +255,7 @@ public class MethodDefinition {
addDebugInfo();
}
private void addMethodItemsForInstruction(int offset, InstructionField instructionField) {
Instruction instruction = instructionField.getInstruction();
private void addMethodItemsForInstruction(int offset, Instruction instruction) {
switch (instruction.getFormat()) {
case Format10t:
instructions.add(new Instruction10tMethodItem(offset, (Instruction10t)instruction));
@ -321,11 +321,11 @@ public class MethodDefinition {
return;
case Format31t:
instructions.add(new Instruction31tMethodItem(offset, (Instruction31t)instruction));
if (instruction.getOpcode() == Opcode.FILL_ARRAY_DATA) {
if (instruction.opcode == Opcode.FILL_ARRAY_DATA) {
labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "array_"));
} else if (instruction.getOpcode() == Opcode.PACKED_SWITCH) {
} else if (instruction.opcode == Opcode.PACKED_SWITCH) {
labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "pswitch_data_"));
} else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) {
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "sswitch_data_"));
}
return;
@ -346,36 +346,50 @@ public class MethodDefinition {
return;
case PackedSwitchData:
{
Integer baseAddress = packedSwitchMap.get(offset);
final Integer baseAddress = packedSwitchMap.get(offset);
if (baseAddress != null) {
PackedSwitchDataPseudoInstruction packedSwitchInstruction =
(PackedSwitchDataPseudoInstruction)instruction;
instructions.add(new PackedSwitchMethodItem(offset,
(PackedSwitchDataPseudoInstruction)instruction, baseAddress));
for (int target: ((PackedSwitchDataPseudoInstruction)instruction).getTargets()) {
labels.add(new LabelMethodItem(baseAddress + target, "pswitch_"));
packedSwitchInstruction, baseAddress));
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator =
packedSwitchInstruction.getTargets();
while (iterator.hasNext()) {
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
labels.add(new LabelMethodItem(baseAddress + target.target, "pswitch_"));
}
}
return;
}
case SparseSwitchData:
{
Integer baseAddress = sparseSwitchMap.get(offset);
final Integer baseAddress = sparseSwitchMap.get(offset);
if (baseAddress != null) {
SparseSwitchDataPseudoInstruction sparseSwitchInstruction =
(SparseSwitchDataPseudoInstruction)instruction;
instructions.add(new SparseSwitchMethodItem(offset,
(SparseSwitchDataPseudoInstruction)instruction, baseAddress));
for (int target: ((SparseSwitchDataPseudoInstruction)instruction).getTargets()) {
labels.add(new LabelMethodItem(baseAddress + target, "sswitch_"));
sparseSwitchInstruction, baseAddress));
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator =
sparseSwitchInstruction.getTargets();
while (iterator.hasNext()) {
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
labels.add(new LabelMethodItem(baseAddress + target.target, "sswitch_"));
}
}
return;
}
}
}
private void addTries() {
for (CodeItem.TryItem tryItem: codeItem.getTries()) {
int startAddress = tryItem.getStartAddress();
int endAddress = tryItem.getEndAddress();
int startAddress = tryItem.startAddress;
int endAddress = tryItem.startAddress + tryItem.instructionCount;
/**
* The end address points to the address immediately after the end of the last
@ -407,7 +421,7 @@ public class MethodDefinition {
int lastInstructionOffset = instructions.get(index).getOffset();
//add the catch all handler if it exists
int catchAllAddress = tryItem.getHandler().getCatchAllAddress();
int catchAllAddress = tryItem.encodedCatchHandler.catchAllHandlerAddress;
if (catchAllAddress != -1) {
CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, null, startAddress,
endAddress, catchAllAddress) {
@ -427,17 +441,17 @@ public class MethodDefinition {
//add the rest of the handlers
//TODO: find adjacent handlers for the same type and combine them
for (CodeItem.EncodedTypeAddrPair handler: tryItem.getHandler().getHandlers()) {
for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
//use the offset from the last covered instruction
CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset,
handler.getTypeReferenceField(), startAddress, endAddress, handler.getHandlerAddress());
handler.exceptionType, startAddress, endAddress, handler.handlerAddress);
catches.add(catchMethodItem);
labels.add(new LabelMethodItem(startAddress, "try_start_"));
//use the offset from the last covered instruction, but make the label
//name refer to the address of the next instruction
labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress));
labels.add(new LabelMethodItem(handler.getHandlerAddress(), "handler_"));
labels.add(new LabelMethodItem(handler.handlerAddress, "handler_"));
}
}
}
@ -448,48 +462,65 @@ public class MethodDefinition {
return;
}
DebugInfoDecoder decoder = new DebugInfoDecoder(debugInfoItem, new DebugInfoDelegate(),
codeItem.getRegisterCount());
decoder.decode();
}
DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
@Override
public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
TypeIdItem type) {
debugItems.add(new LocalDebugMethodItem(codeAddress, "StartLocal", -1, registerNum, name,
type, null));
}
private class DebugInfoDelegate implements DebugInfoDecoder.DebugInfoDelegate {
@Override
public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum,
StringIdItem name, TypeIdItem type,
StringIdItem signature) {
debugItems.add(new LocalDebugMethodItem(codeAddress, "StartLocal", -1, registerNum, name,
type, signature));
}
public void endPrologue(int address) {
debugItems.add(new DebugMethodItem(address, "EndPrologue", -4));
}
@Override
public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name,
TypeIdItem type, StringIdItem signature) {
debugItems.add(new LocalDebugMethodItem(codeAddress, "EndLocal", -1, registerNum, name,
type, signature));
}
public void startEpilogue(int address) {
debugItems.add(new DebugMethodItem(address, "StartEpilogue", -4));
}
@Override
public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
TypeIdItem type, StringIdItem signature) {
debugItems.add(new LocalDebugMethodItem(codeAddress, "RestartLocal", -1, registerNum, name,
type, signature));
}
public void startLocal(int address, DebugInfoDecoder.Local local) {
debugItems.add(new LocalDebugMethodItem(address, "StartLocal", -1, local));
}
@Override
public void ProcessSetPrologueEnd(int codeAddress) {
debugItems.add(new DebugMethodItem(codeAddress, "EndPrologue", -4));
}
public void endLocal(int address, DebugInfoDecoder.Local local) {
debugItems.add(new LocalDebugMethodItem(address, "EndLocal", -1, local));
}
@Override
public void ProcessSetEpilogueBegin(int codeAddress) {
debugItems.add(new DebugMethodItem(codeAddress, "StartEpilogue", -4));
}
public void restartLocal(int address, DebugInfoDecoder.Local local) {
debugItems.add(new LocalDebugMethodItem(address, "RestartLocal", -1, local));
}
@Override
public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
debugItems.add(new DebugMethodItem(codeAddress, "SetFile", -3) {
public String getFileName() {
return name.getStringValue();
}
});
}
public void setFile(int address, final StringIdItem fileName) {
debugItems.add(new DebugMethodItem(address, "SetFile", -3) {
public String getFileName() {
return fileName.getStringValue();
}
});
}
public void line(int address, final int line) {
debugItems.add(new DebugMethodItem(address, "Line", -2) {
public int getLine() {
return line;
}
});
}
@Override
public void ProcessLineEmit(int codeAddress, final int line) {
debugItems.add(new DebugMethodItem(codeAddress, "Line", -2) {
public int getLine() {
return line;
}
});
}
});
}
}
}

View File

@ -53,7 +53,7 @@ public class ParameterAdaptor {
}
List<AnnotationAdaptor> annotations = new ArrayList<AnnotationAdaptor>();
for (AnnotationItem annotationItem: parameterAnnotations.getAnnotationItems()) {
for (AnnotationItem annotationItem: parameterAnnotations.getAnnotations()) {
annotations.add(new AnnotationAdaptor(annotationItem));
}
return annotations;

View File

@ -30,14 +30,14 @@ package org.jf.baksmali.Adaptors.Reference;
import org.jf.dexlib.*;
public abstract class Reference<T extends IndexedItem> {
public abstract class Reference<T extends Item> {
protected T item;
protected Reference(T item) {
this.item = item;
}
public static Reference makeReference(IndexedItem item) {
public static Reference makeReference(Item item) {
switch (item.getItemType()) {
case TYPE_METHOD_ID_ITEM:
return new MethodReference((MethodIdItem)item);

View File

@ -0,0 +1,42 @@
/*
* [The "BSD licence"]
* Copyright (c) 2009 Ben Gruver
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jf.baksmali.Renderers;
import org.antlr.stringtemplate.AttributeRenderer;
import org.jf.dexlib.StringIdItem;
public class StringIdItemRenderer implements AttributeRenderer {
public String toString(Object o) {
return ((StringIdItem)o).getStringValue();
}
public String toString(Object o, String s) {
return toString(o);
}
}

View File

@ -34,6 +34,7 @@ import org.jf.baksmali.Adaptors.ClassDefinition;
import org.jf.baksmali.Renderers.*;
import org.jf.dexlib.DexFile;
import org.jf.dexlib.ClassDefItem;
import org.jf.dexlib.StringIdItem;
import java.io.*;
@ -57,7 +58,8 @@ public class baksmali {
templates.registerRenderer(Byte.class, new ByteRenderer());
templates.registerRenderer(Float.class, new FloatRenderer());
templates.registerRenderer(Character.class, new CharRenderer());
templates.registerRenderer(StringIdItem.class, new StringIdItemRenderer());
for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
/**

View File

@ -262,21 +262,21 @@ Format51l(Instruction) ::=
ArrayData(Instruction) ::=
<<
.array-data <Instruction.ElementWidth>
<Instruction.Values: {<it.ByteArray; format="unsigned",separator=" ">}; separator="\n">
<Instruction.Values: {<it; format="unsigned",separator=" ">}; separator="\n">
.end array-data
>>
PackedSwitchData(Instruction) ::=
<<
.packed-switch <Instruction.FirstKey>
<Instruction.Targets: {pswitch_<it>:}; separator="\n">
<Instruction.Targets: {pswitch_<it.Target>:}; separator="\n">
.end packed-switch
>>
SparseSwitchData(Instruction) ::=
<<
.sparse-switch
<Instruction.Keys,Instruction.Targets:{key,target | <key> -> sswitch_<target>:}; separator="\n">
<Instruction.Targets:{<it.Value> -> sswitch_<it.Target>:}; separator="\n">
.end sparse-switch
>>