1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.writer.builder; 33 34 import com.google.common.base.Function; 35 import com.google.common.collect.ImmutableList; 36 import com.google.common.collect.Iterators; 37 import com.google.common.collect.Lists; 38 import org.jf.dexlib2.ValueType; 39 import org.jf.dexlib2.iface.Annotation; 40 import org.jf.dexlib2.iface.MethodImplementation; 41 import org.jf.dexlib2.iface.MethodParameter; 42 import org.jf.dexlib2.iface.reference.*; 43 import org.jf.dexlib2.iface.value.*; 44 import org.jf.dexlib2.writer.DexWriter; 45 import org.jf.dexlib2.writer.builder.BuilderEncodedValues.*; 46 import org.jf.util.ExceptionWithContext; 47 48 import javax.annotation.Nonnull; 49 import javax.annotation.Nullable; 50 import java.io.IOException; 51 import java.util.Collections; 52 import java.util.Iterator; 53 import java.util.List; 54 import java.util.Set; 55 56 public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringReference, BuilderTypeReference, 57 BuilderTypeReference, BuilderProtoReference, BuilderFieldReference, BuilderMethodReference, 58 BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod, 59 BuilderEncodedValue, BuilderAnnotationElement> { 60 61 private final BuilderContext context; 62 63 public static DexBuilder makeDexBuilder() { 64 BuilderContext context = new BuilderContext(); 65 return new DexBuilder(15, context); 66 } 67 68 public static DexBuilder makeDexBuilder(int api) { 69 BuilderContext context = new BuilderContext(); 70 return new DexBuilder(api, context); 71 } 72 73 private DexBuilder(int api, @Nonnull BuilderContext context) { 74 super(api, context.stringPool, context.typePool, context.protoPool, 75 context.fieldPool, context.methodPool, context.classPool, context.typeListPool, context.annotationPool, 76 context.annotationSetPool); 77 this.context = context; 78 } 79 80 @Nonnull public BuilderField internField(@Nonnull String definingClass, 81 @Nonnull String name, 82 @Nonnull String type, 83 int accessFlags, 84 @Nullable EncodedValue initialValue, 85 @Nonnull Set<? extends Annotation> annotations) { 86 return new BuilderField(context.fieldPool.internField(definingClass, name, type), 87 accessFlags, 88 context.internNullableEncodedValue(initialValue), 89 context.annotationSetPool.internAnnotationSet(annotations)); 90 } 91 92 @Nonnull public BuilderMethod internMethod(@Nonnull String definingClass, 93 @Nonnull String name, 94 @Nullable List<? extends MethodParameter> parameters, 95 @Nonnull String returnType, 96 int accessFlags, 97 @Nonnull Set<? extends Annotation> annotations, 98 @Nullable MethodImplementation methodImplementation) { 99 if (parameters == null) { 100 parameters = ImmutableList.of(); 101 } 102 return new BuilderMethod(context.methodPool.internMethod(definingClass, name, parameters, returnType), 103 internMethodParameters(parameters), 104 accessFlags, 105 context.annotationSetPool.internAnnotationSet(annotations), 106 methodImplementation); 107 } 108 109 @Nonnull public BuilderClassDef internClassDef(@Nonnull String type, 110 int accessFlags, 111 @Nullable String superclass, 112 @Nullable List<String> interfaces, 113 @Nullable String sourceFile, 114 @Nonnull Set<? extends Annotation> annotations, 115 @Nullable Iterable<? extends BuilderField> fields, 116 @Nullable Iterable<? extends BuilderMethod> methods) { 117 if (interfaces == null) { 118 interfaces = ImmutableList.of(); 119 } else { 120 interfaces = Lists.newArrayList(interfaces); 121 Collections.sort(interfaces); 122 String prev = null; 123 Iterator<String> interfaceIterator = interfaces.iterator(); 124 while (interfaceIterator.hasNext()) { 125 String iface = interfaceIterator.next(); 126 if (prev != null && iface.equals(prev)) { 127 interfaceIterator.remove(); 128 } 129 prev = iface; 130 } 131 } 132 133 return context.classPool.internClass(new BuilderClassDef(context.typePool.internType(type), 134 accessFlags, 135 context.typePool.internNullableType(superclass), 136 context.typeListPool.internTypeList(interfaces), 137 context.stringPool.internNullableString(sourceFile), 138 context.annotationSetPool.internAnnotationSet(annotations), 139 fields, 140 methods)); 141 } 142 143 @Nonnull public BuilderStringReference internStringReference(@Nonnull String string) { 144 return context.stringPool.internString(string); 145 } 146 147 @Nullable public BuilderStringReference internNullableStringReference(@Nullable String string) { 148 if (string != null) { 149 return internStringReference(string); 150 } 151 return null; 152 } 153 154 @Nonnull public BuilderTypeReference internTypeReference(@Nonnull String type) { 155 return context.typePool.internType(type); 156 } 157 158 @Nullable public BuilderTypeReference internNullableTypeReference(@Nullable String type) { 159 if (type != null) { 160 return internTypeReference(type); 161 } 162 return null; 163 } 164 165 @Nonnull public BuilderFieldReference internFieldReference(@Nonnull FieldReference field) { 166 return context.fieldPool.internField(field); 167 } 168 169 @Nonnull public BuilderMethodReference internMethodReference(@Nonnull MethodReference method) { 170 return context.methodPool.internMethod(method); 171 } 172 173 @Nonnull public BuilderReference internReference(@Nonnull Reference reference) { 174 if (reference instanceof StringReference) { 175 return internStringReference(((StringReference)reference).getString()); 176 } 177 if (reference instanceof TypeReference) { 178 return internTypeReference(((TypeReference)reference).getType()); 179 } 180 if (reference instanceof MethodReference) { 181 return internMethodReference((MethodReference)reference); 182 } 183 if (reference instanceof FieldReference) { 184 return internFieldReference((FieldReference)reference); 185 } 186 throw new IllegalArgumentException("Could not determine type of reference"); 187 } 188 189 @Nonnull private List<BuilderMethodParameter> internMethodParameters( 190 @Nullable List<? extends MethodParameter> methodParameters) { 191 if (methodParameters == null) { 192 return ImmutableList.of(); 193 } 194 return ImmutableList.copyOf(Iterators.transform(methodParameters.iterator(), 195 new Function<MethodParameter, BuilderMethodParameter>() { 196 @Nullable @Override public BuilderMethodParameter apply(MethodParameter input) { 197 return internMethodParameter(input); 198 } 199 })); 200 } 201 202 @Nonnull private BuilderMethodParameter internMethodParameter(@Nonnull MethodParameter methodParameter) { 203 return new BuilderMethodParameter( 204 context.typePool.internType(methodParameter.getType()), 205 context.stringPool.internNullableString(methodParameter.getName()), 206 context.annotationSetPool.internAnnotationSet(methodParameter.getAnnotations())); 207 } 208 209 @Override protected void writeEncodedValue(@Nonnull InternalEncodedValueWriter writer, 210 @Nonnull BuilderEncodedValue encodedValue) throws IOException { 211 switch (encodedValue.getValueType()) { 212 case ValueType.ANNOTATION: 213 BuilderAnnotationEncodedValue annotationEncodedValue = (BuilderAnnotationEncodedValue)encodedValue; 214 writer.writeAnnotation(annotationEncodedValue.typeReference, annotationEncodedValue.elements); 215 break; 216 case ValueType.ARRAY: 217 BuilderArrayEncodedValue arrayEncodedValue = (BuilderArrayEncodedValue)encodedValue; 218 writer.writeArray(arrayEncodedValue.elements); 219 break; 220 case ValueType.BOOLEAN: 221 writer.writeBoolean(((BooleanEncodedValue)encodedValue).getValue()); 222 break; 223 case ValueType.BYTE: 224 writer.writeByte(((ByteEncodedValue)encodedValue).getValue()); 225 break; 226 case ValueType.CHAR: 227 writer.writeChar(((CharEncodedValue)encodedValue).getValue()); 228 break; 229 case ValueType.DOUBLE: 230 writer.writeDouble(((DoubleEncodedValue)encodedValue).getValue()); 231 break; 232 case ValueType.ENUM: 233 writer.writeEnum(((BuilderEnumEncodedValue)encodedValue).getValue()); 234 break; 235 case ValueType.FIELD: 236 writer.writeField(((BuilderFieldEncodedValue)encodedValue).fieldReference); 237 break; 238 case ValueType.FLOAT: 239 writer.writeFloat(((FloatEncodedValue)encodedValue).getValue()); 240 break; 241 case ValueType.INT: 242 writer.writeInt(((IntEncodedValue)encodedValue).getValue()); 243 break; 244 case ValueType.LONG: 245 writer.writeLong(((LongEncodedValue)encodedValue).getValue()); 246 break; 247 case ValueType.METHOD: 248 writer.writeMethod(((BuilderMethodEncodedValue)encodedValue).methodReference); 249 break; 250 case ValueType.NULL: 251 writer.writeNull(); 252 break; 253 case ValueType.SHORT: 254 writer.writeShort(((ShortEncodedValue)encodedValue).getValue()); 255 break; 256 case ValueType.STRING: 257 writer.writeString(((BuilderStringEncodedValue)encodedValue).stringReference); 258 break; 259 case ValueType.TYPE: 260 writer.writeType(((BuilderTypeEncodedValue)encodedValue).typeReference); 261 break; 262 default: 263 throw new ExceptionWithContext("Unrecognized value type: %d", encodedValue.getValueType()); 264 } 265 } 266 } 267