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