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 32package org.jf.dexlib2.writer.builder; 33 34import com.google.common.base.Function; 35import com.google.common.base.Predicate; 36import com.google.common.collect.*; 37import org.jf.dexlib2.DebugItemType; 38import org.jf.dexlib2.builder.MutableMethodImplementation; 39import org.jf.dexlib2.iface.ExceptionHandler; 40import org.jf.dexlib2.iface.Field; 41import org.jf.dexlib2.iface.MethodImplementation; 42import org.jf.dexlib2.iface.TryBlock; 43import org.jf.dexlib2.iface.debug.*; 44import org.jf.dexlib2.iface.instruction.Instruction; 45import org.jf.dexlib2.iface.reference.StringReference; 46import org.jf.dexlib2.iface.reference.TypeReference; 47import org.jf.dexlib2.iface.value.EncodedValue; 48import org.jf.dexlib2.util.EncodedValueUtils; 49import org.jf.dexlib2.writer.ClassSection; 50import org.jf.dexlib2.writer.DebugWriter; 51import org.jf.dexlib2.writer.builder.BuilderEncodedValues.BuilderEncodedValue; 52import org.jf.util.AbstractForwardSequentialList; 53import org.jf.util.CollectionUtils; 54import org.jf.util.ExceptionWithContext; 55 56import javax.annotation.Nonnull; 57import javax.annotation.Nullable; 58import java.io.IOException; 59import java.util.*; 60import java.util.Map.Entry; 61import java.util.concurrent.ConcurrentMap; 62 63public class BuilderClassPool extends BaseBuilderPool implements ClassSection<BuilderStringReference, 64 BuilderTypeReference, BuilderTypeList, BuilderClassDef, BuilderField, BuilderMethod, BuilderAnnotationSet, 65 BuilderEncodedValue> { 66 @Nonnull private final ConcurrentMap<String, BuilderClassDef> internedItems = 67 Maps.newConcurrentMap(); 68 69 public BuilderClassPool(@Nonnull DexBuilder dexBuilder) { 70 super(dexBuilder); 71 } 72 73 @Nonnull BuilderClassDef internClass(@Nonnull BuilderClassDef classDef) { 74 BuilderClassDef prev = internedItems.put(classDef.getType(), classDef); 75 if (prev != null) { 76 throw new ExceptionWithContext("Class %s has already been interned", classDef.getType()); 77 } 78 return classDef; 79 } 80 81 private ImmutableList<BuilderClassDef> sortedClasses = null; 82 @Nonnull @Override public Collection<? extends BuilderClassDef> getSortedClasses() { 83 if (sortedClasses == null) { 84 sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values()); 85 } 86 return sortedClasses; 87 } 88 89 @Nullable @Override 90 public Entry<? extends BuilderClassDef, Integer> getClassEntryByType(@Nullable BuilderTypeReference type) { 91 if (type == null) { 92 return null; 93 } 94 95 final BuilderClassDef classDef = internedItems.get(type.getType()); 96 if (classDef == null) { 97 return null; 98 } 99 100 return new Map.Entry<BuilderClassDef, Integer>() { 101 @Override public BuilderClassDef getKey() { 102 return classDef; 103 } 104 105 @Override public Integer getValue() { 106 return classDef.classDefIndex; 107 } 108 109 @Override public Integer setValue(Integer value) { 110 return classDef.classDefIndex = value; 111 } 112 }; 113 } 114 115 @Nonnull @Override public BuilderTypeReference getType(@Nonnull BuilderClassDef builderClassDef) { 116 return builderClassDef.type; 117 } 118 119 @Override public int getAccessFlags(@Nonnull BuilderClassDef builderClassDef) { 120 return builderClassDef.accessFlags; 121 } 122 123 @Nullable @Override public BuilderTypeReference getSuperclass(@Nonnull BuilderClassDef builderClassDef) { 124 return builderClassDef.superclass; 125 } 126 127 @Nullable @Override public BuilderTypeList getInterfaces(@Nonnull BuilderClassDef builderClassDef) { 128 return builderClassDef.interfaces; 129 } 130 131 @Nullable @Override public BuilderStringReference getSourceFile(@Nonnull BuilderClassDef builderClassDef) { 132 return builderClassDef.sourceFile; 133 } 134 135 private static final Predicate<Field> HAS_INITIALIZER = new Predicate<Field>() { 136 @Override 137 public boolean apply(Field input) { 138 EncodedValue encodedValue = input.getInitialValue(); 139 return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue); 140 } 141 }; 142 143 private static final Function<BuilderField, BuilderEncodedValue> GET_INITIAL_VALUE = 144 new Function<BuilderField, BuilderEncodedValue>() { 145 @Override 146 public BuilderEncodedValue apply(BuilderField input) { 147 BuilderEncodedValue initialValue = input.getInitialValue(); 148 if (initialValue == null) { 149 return BuilderEncodedValues.defaultValueForType(input.getType()); 150 } 151 return initialValue; 152 } 153 }; 154 155 @Nullable @Override 156 public Collection<? extends BuilderEncodedValue> getStaticInitializers(@Nonnull BuilderClassDef classDef) { 157 final SortedSet<BuilderField> sortedStaticFields = classDef.getStaticFields(); 158 159 final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER); 160 if (lastIndex > -1) { 161 return new AbstractCollection<BuilderEncodedValue>() { 162 @Nonnull @Override public Iterator<BuilderEncodedValue> iterator() { 163 Iterable<BuilderField> fields = Iterables.limit(sortedStaticFields, lastIndex + 1); 164 return Iterables.transform(fields, GET_INITIAL_VALUE).iterator(); 165 } 166 167 @Override public int size() { 168 return lastIndex+1; 169 } 170 }; 171 } 172 return null; 173 } 174 175 @Nonnull @Override 176 public Collection<? extends BuilderField> getSortedStaticFields(@Nonnull BuilderClassDef builderClassDef) { 177 return builderClassDef.getStaticFields(); 178 } 179 180 @Nonnull @Override 181 public Collection<? extends BuilderField> getSortedInstanceFields(@Nonnull BuilderClassDef builderClassDef) { 182 return builderClassDef.getInstanceFields(); 183 } 184 185 @Nonnull @Override 186 public Collection<? extends BuilderField> getSortedFields(@Nonnull BuilderClassDef builderClassDef) { 187 return builderClassDef.getFields(); 188 } 189 190 @Nonnull @Override 191 public Collection<? extends BuilderMethod> getSortedDirectMethods(@Nonnull BuilderClassDef builderClassDef) { 192 return builderClassDef.getDirectMethods(); 193 } 194 195 @Nonnull @Override 196 public Collection<? extends BuilderMethod> getSortedVirtualMethods(@Nonnull BuilderClassDef builderClassDef) { 197 return builderClassDef.getVirtualMethods(); 198 } 199 200 @Nonnull @Override 201 public Collection<? extends BuilderMethod> getSortedMethods(@Nonnull BuilderClassDef builderClassDef) { 202 return builderClassDef.getMethods(); 203 } 204 205 @Override public int getFieldAccessFlags(@Nonnull BuilderField builderField) { 206 return builderField.accessFlags; 207 } 208 209 @Override public int getMethodAccessFlags(@Nonnull BuilderMethod builderMethod) { 210 return builderMethod.accessFlags; 211 } 212 213 @Nullable @Override public BuilderAnnotationSet getClassAnnotations(@Nonnull BuilderClassDef builderClassDef) { 214 if (builderClassDef.annotations.isEmpty()) { 215 return null; 216 } 217 return builderClassDef.annotations; 218 } 219 220 @Nullable @Override public BuilderAnnotationSet getFieldAnnotations(@Nonnull BuilderField builderField) { 221 if (builderField.annotations.isEmpty()) { 222 return null; 223 } 224 return builderField.annotations; 225 } 226 227 @Nullable @Override public BuilderAnnotationSet getMethodAnnotations(@Nonnull BuilderMethod builderMethod) { 228 if (builderMethod.annotations.isEmpty()) { 229 return null; 230 } 231 return builderMethod.annotations; 232 } 233 234 private static final Predicate<BuilderMethodParameter> HAS_PARAMETER_ANNOTATIONS = 235 new Predicate<BuilderMethodParameter>() { 236 @Override 237 public boolean apply(BuilderMethodParameter input) { 238 return input.getAnnotations().size() > 0; 239 } 240 }; 241 242 private static final Function<BuilderMethodParameter, BuilderAnnotationSet> PARAMETER_ANNOTATIONS = 243 new Function<BuilderMethodParameter, BuilderAnnotationSet>() { 244 @Override 245 public BuilderAnnotationSet apply(BuilderMethodParameter input) { 246 return input.getAnnotations(); 247 } 248 }; 249 250 @Nullable @Override public List<? extends BuilderAnnotationSet> getParameterAnnotations( 251 @Nonnull final BuilderMethod method) { 252 final List<? extends BuilderMethodParameter> parameters = method.getParameters(); 253 boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS); 254 255 if (hasParameterAnnotations) { 256 return new AbstractForwardSequentialList<BuilderAnnotationSet>() { 257 @Nonnull @Override public Iterator<BuilderAnnotationSet> iterator() { 258 return Iterables.transform(parameters, PARAMETER_ANNOTATIONS).iterator(); 259 } 260 261 @Override public int size() { 262 return parameters.size(); 263 } 264 }; 265 } 266 return null; 267 } 268 269 @Nullable @Override 270 public Iterable<? extends DebugItem> getDebugItems(@Nonnull BuilderMethod builderMethod) { 271 MethodImplementation impl = builderMethod.getImplementation(); 272 if (impl == null) { 273 return null; 274 } 275 return impl.getDebugItems(); 276 } 277 278 @Nullable @Override 279 public Iterable<? extends BuilderStringReference> getParameterNames(@Nonnull BuilderMethod method) { 280 return Iterables.transform(method.getParameters(), new Function<BuilderMethodParameter, BuilderStringReference>() { 281 @Nullable @Override public BuilderStringReference apply(BuilderMethodParameter input) { 282 return input.name; 283 } 284 }); 285 } 286 287 @Override public int getRegisterCount(@Nonnull BuilderMethod builderMethod) { 288 MethodImplementation impl = builderMethod.getImplementation(); 289 if (impl == null) { 290 return 0; 291 } 292 return impl.getRegisterCount(); 293 } 294 295 @Nullable @Override 296 public Iterable<? extends Instruction> getInstructions(@Nonnull BuilderMethod builderMethod) { 297 MethodImplementation impl = builderMethod.getImplementation(); 298 if (impl == null) { 299 return null; 300 } 301 return impl.getInstructions(); 302 } 303 304 @Nonnull @Override 305 public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks(@Nonnull BuilderMethod builderMethod) { 306 MethodImplementation impl = builderMethod.getImplementation(); 307 if (impl == null) { 308 return ImmutableList.of(); 309 } 310 return impl.getTryBlocks(); 311 } 312 313 @Nullable @Override public BuilderTypeReference getExceptionType(@Nonnull ExceptionHandler handler) { 314 return checkTypeReference(handler.getExceptionTypeReference()); 315 } 316 317 @Nonnull @Override 318 public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull BuilderMethod builderMethod) { 319 MethodImplementation impl = builderMethod.getImplementation(); 320 if (impl instanceof MutableMethodImplementation) { 321 return (MutableMethodImplementation)impl; 322 } 323 return new MutableMethodImplementation(impl); 324 } 325 326 @Override public void setEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { 327 builderClassDef.encodedArrayOffset = offset; 328 } 329 330 @Override public int getEncodedArrayOffset(@Nonnull BuilderClassDef builderClassDef) { 331 return builderClassDef.encodedArrayOffset; 332 } 333 334 @Override public void setAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef, int offset) { 335 builderClassDef.annotationDirectoryOffset = offset; 336 } 337 338 @Override public int getAnnotationDirectoryOffset(@Nonnull BuilderClassDef builderClassDef) { 339 return builderClassDef.annotationDirectoryOffset; 340 } 341 342 @Override public void setAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod, int offset) { 343 builderMethod.annotationSetRefListOffset = offset; 344 } 345 346 @Override public int getAnnotationSetRefListOffset(@Nonnull BuilderMethod builderMethod) { 347 return builderMethod.annotationSetRefListOffset; 348 } 349 350 @Override public void setCodeItemOffset(@Nonnull BuilderMethod builderMethod, int offset) { 351 builderMethod.codeItemOffset = offset; 352 } 353 354 @Override public int getCodeItemOffset(@Nonnull BuilderMethod builderMethod) { 355 return builderMethod.codeItemOffset; 356 } 357 358 @Nullable private BuilderStringReference checkStringReference(@Nullable StringReference stringReference) { 359 if (stringReference == null) { 360 return null; 361 } 362 try { 363 return (BuilderStringReference)stringReference; 364 } catch (ClassCastException ex) { 365 throw new IllegalStateException("Only StringReference instances returned by " + 366 "DexBuilder.internStringReference or DexBuilder.internNullableStringReference may be used."); 367 } 368 } 369 370 @Nullable private BuilderTypeReference checkTypeReference(@Nullable TypeReference typeReference) { 371 if (typeReference == null) { 372 return null; 373 } 374 try { 375 return (BuilderTypeReference)typeReference; 376 } catch (ClassCastException ex) { 377 throw new IllegalStateException("Only TypeReference instances returned by " + 378 "DexBuilder.internTypeReference or DexBuilder.internNullableTypeReference may be used."); 379 } 380 } 381 382 @Override 383 public void writeDebugItem(@Nonnull DebugWriter<BuilderStringReference, BuilderTypeReference> writer, 384 DebugItem debugItem) throws IOException { 385 switch (debugItem.getDebugItemType()) { 386 case DebugItemType.START_LOCAL: { 387 StartLocal startLocal = (StartLocal)debugItem; 388 writer.writeStartLocal(startLocal.getCodeAddress(), 389 startLocal.getRegister(), 390 checkStringReference(startLocal.getNameReference()), 391 checkTypeReference(startLocal.getTypeReference()), 392 checkStringReference(startLocal.getSignatureReference())); 393 break; 394 } 395 case DebugItemType.END_LOCAL: { 396 EndLocal endLocal = (EndLocal)debugItem; 397 writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister()); 398 break; 399 } 400 case DebugItemType.RESTART_LOCAL: { 401 RestartLocal restartLocal = (RestartLocal)debugItem; 402 writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister()); 403 break; 404 } 405 case DebugItemType.PROLOGUE_END: { 406 writer.writePrologueEnd(debugItem.getCodeAddress()); 407 break; 408 } 409 case DebugItemType.EPILOGUE_BEGIN: { 410 writer.writeEpilogueBegin(debugItem.getCodeAddress()); 411 break; 412 } 413 case DebugItemType.LINE_NUMBER: { 414 LineNumber lineNumber = (LineNumber)debugItem; 415 writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber()); 416 break; 417 } 418 case DebugItemType.SET_SOURCE_FILE: { 419 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 420 writer.writeSetSourceFile(setSourceFile.getCodeAddress(), 421 checkStringReference(setSourceFile.getSourceFileReference())); 422 break; 423 } 424 default: 425 throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); 426 } 427 } 428 429 @Override public int getItemIndex(@Nonnull BuilderClassDef builderClassDef) { 430 return builderClassDef.classDefIndex; 431 } 432 433 @Nonnull @Override public Collection<? extends Entry<? extends BuilderClassDef, Integer>> getItems() { 434 return new BuilderMapEntryCollection<BuilderClassDef>(internedItems.values()) { 435 @Override protected int getValue(@Nonnull BuilderClassDef key) { 436 return key.classDefIndex; 437 } 438 439 @Override protected int setValue(@Nonnull BuilderClassDef key, int value) { 440 int prev = key.classDefIndex; 441 key.classDefIndex = value; 442 return prev; 443 } 444 }; 445 } 446 447 @Override public int getItemCount() { 448 return internedItems.size(); 449 } 450} 451