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.pool; 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.ReferenceType; 39import org.jf.dexlib2.builder.MutableMethodImplementation; 40import org.jf.dexlib2.iface.*; 41import org.jf.dexlib2.iface.debug.*; 42import org.jf.dexlib2.iface.instruction.Instruction; 43import org.jf.dexlib2.iface.instruction.ReferenceInstruction; 44import org.jf.dexlib2.iface.reference.*; 45import org.jf.dexlib2.iface.value.EncodedValue; 46import org.jf.dexlib2.immutable.value.ImmutableEncodedValueFactory; 47import org.jf.dexlib2.util.EncodedValueUtils; 48import org.jf.dexlib2.util.ReferenceUtil; 49import org.jf.dexlib2.writer.ClassSection; 50import org.jf.dexlib2.writer.DebugWriter; 51import org.jf.util.AbstractForwardSequentialList; 52import org.jf.util.CollectionUtils; 53import org.jf.util.ExceptionWithContext; 54 55import javax.annotation.Nonnull; 56import javax.annotation.Nullable; 57import java.io.IOException; 58import java.util.*; 59import java.util.Map.Entry; 60 61public class ClassPool extends BasePool<String, PoolClassDef> implements ClassSection<CharSequence, CharSequence, 62 TypeListPool.Key<? extends Collection<? extends CharSequence>>, PoolClassDef, Field, PoolMethod, 63 Set<? extends Annotation>, EncodedValue> { 64 65 public ClassPool(@Nonnull DexPool dexPool) { 66 super(dexPool); 67 } 68 69 public void intern(@Nonnull ClassDef classDef) { 70 PoolClassDef poolClassDef = new PoolClassDef(classDef); 71 72 PoolClassDef prev = internedItems.put(poolClassDef.getType(), poolClassDef); 73 if (prev != null) { 74 throw new ExceptionWithContext("Class %s has already been interned", poolClassDef.getType()); 75 } 76 77 dexPool.typeSection.intern(poolClassDef.getType()); 78 dexPool.typeSection.internNullable(poolClassDef.getSuperclass()); 79 dexPool.typeListSection.intern(poolClassDef.getInterfaces()); 80 dexPool.stringSection.internNullable(poolClassDef.getSourceFile()); 81 82 HashSet<String> fields = new HashSet<String>(); 83 for (Field field: poolClassDef.getFields()) { 84 String fieldDescriptor = ReferenceUtil.getShortFieldDescriptor(field); 85 if (!fields.add(fieldDescriptor)) { 86 throw new ExceptionWithContext("Multiple definitions for field %s->%s", 87 poolClassDef.getType(), fieldDescriptor); 88 } 89 dexPool.fieldSection.intern(field); 90 91 EncodedValue initialValue = field.getInitialValue(); 92 if (initialValue != null) { 93 dexPool.internEncodedValue(initialValue); 94 } 95 96 dexPool.annotationSetSection.intern(field.getAnnotations()); 97 } 98 99 HashSet<String> methods = new HashSet<String>(); 100 for (PoolMethod method: poolClassDef.getMethods()) { 101 String methodDescriptor = ReferenceUtil.getMethodDescriptor(method, true); 102 if (!methods.add(methodDescriptor)) { 103 throw new ExceptionWithContext("Multiple definitions for method %s->%s", 104 poolClassDef.getType(), methodDescriptor); 105 } 106 dexPool.methodSection.intern(method); 107 internCode(method); 108 internDebug(method); 109 dexPool.annotationSetSection.intern(method.getAnnotations()); 110 111 for (MethodParameter parameter: method.getParameters()) { 112 dexPool.annotationSetSection.intern(parameter.getAnnotations()); 113 } 114 } 115 116 dexPool.annotationSetSection.intern(poolClassDef.getAnnotations()); 117 } 118 119 private void internCode(@Nonnull Method method) { 120 // this also handles parameter names, which aren't directly tied to the MethodImplementation, even though the debug items are 121 boolean hasInstruction = false; 122 123 MethodImplementation methodImpl = method.getImplementation(); 124 if (methodImpl != null) { 125 for (Instruction instruction: methodImpl.getInstructions()) { 126 hasInstruction = true; 127 if (instruction instanceof ReferenceInstruction) { 128 Reference reference = ((ReferenceInstruction)instruction).getReference(); 129 switch (instruction.getOpcode().referenceType) { 130 case ReferenceType.STRING: 131 dexPool.stringSection.intern((StringReference)reference); 132 break; 133 case ReferenceType.TYPE: 134 dexPool.typeSection.intern((TypeReference)reference); 135 break; 136 case ReferenceType.FIELD: 137 dexPool.fieldSection.intern((FieldReference) reference); 138 break; 139 case ReferenceType.METHOD: 140 dexPool.methodSection.intern((MethodReference)reference); 141 break; 142 default: 143 throw new ExceptionWithContext("Unrecognized reference type: %d", 144 instruction.getOpcode().referenceType); 145 } 146 } 147 } 148 149 List<? extends TryBlock> tryBlocks = methodImpl.getTryBlocks(); 150 if (!hasInstruction && tryBlocks.size() > 0) { 151 throw new ExceptionWithContext("Method %s has no instructions, but has try blocks.", 152 ReferenceUtil.getMethodDescriptor(method)); 153 } 154 155 for (TryBlock<? extends ExceptionHandler> tryBlock: methodImpl.getTryBlocks()) { 156 for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) { 157 dexPool.typeSection.internNullable(handler.getExceptionType()); 158 } 159 } 160 } 161 } 162 163 private void internDebug(@Nonnull Method method) { 164 for (MethodParameter param: method.getParameters()) { 165 String paramName = param.getName(); 166 if (paramName != null) { 167 dexPool.stringSection.intern(paramName); 168 } 169 } 170 171 MethodImplementation methodImpl = method.getImplementation(); 172 if (methodImpl != null) { 173 for (DebugItem debugItem: methodImpl.getDebugItems()) { 174 switch (debugItem.getDebugItemType()) { 175 case DebugItemType.START_LOCAL: 176 StartLocal startLocal = (StartLocal)debugItem; 177 dexPool.stringSection.internNullable(startLocal.getName()); 178 dexPool.typeSection.internNullable(startLocal.getType()); 179 dexPool.stringSection.internNullable(startLocal.getSignature()); 180 break; 181 case DebugItemType.SET_SOURCE_FILE: 182 dexPool.stringSection.internNullable(((SetSourceFile) debugItem).getSourceFile()); 183 break; 184 } 185 } 186 } 187 } 188 189 private ImmutableList<PoolClassDef> sortedClasses = null; 190 @Nonnull @Override public Collection<? extends PoolClassDef> getSortedClasses() { 191 if (sortedClasses == null) { 192 sortedClasses = Ordering.natural().immutableSortedCopy(internedItems.values()); 193 } 194 return sortedClasses; 195 } 196 197 @Nullable @Override 198 public Map.Entry<? extends PoolClassDef, Integer> getClassEntryByType(@Nullable CharSequence name) { 199 if (name == null) { 200 return null; 201 } 202 203 final PoolClassDef classDef = internedItems.get(name.toString()); 204 if (classDef == null) { 205 return null; 206 } 207 208 return new Map.Entry<PoolClassDef, Integer>() { 209 @Override public PoolClassDef getKey() { 210 return classDef; 211 } 212 213 @Override public Integer getValue() { 214 return classDef.classDefIndex; 215 } 216 217 @Override public Integer setValue(Integer value) { 218 return classDef.classDefIndex = value; 219 } 220 }; 221 } 222 223 @Nonnull @Override public CharSequence getType(@Nonnull PoolClassDef classDef) { 224 return classDef.getType(); 225 } 226 227 @Override public int getAccessFlags(@Nonnull PoolClassDef classDef) { 228 return classDef.getAccessFlags(); 229 } 230 231 @Nullable @Override public CharSequence getSuperclass(@Nonnull PoolClassDef classDef) { 232 return classDef.getSuperclass(); 233 } 234 235 @Nullable @Override public TypeListPool.Key<List<String>> getInterfaces(@Nonnull PoolClassDef classDef) { 236 return classDef.interfaces; 237 } 238 239 @Nullable @Override public CharSequence getSourceFile(@Nonnull PoolClassDef classDef) { 240 return classDef.getSourceFile(); 241 } 242 243 private static final Predicate<Field> HAS_INITIALIZER = new Predicate<Field>() { 244 @Override 245 public boolean apply(Field input) { 246 EncodedValue encodedValue = input.getInitialValue(); 247 return encodedValue != null && !EncodedValueUtils.isDefaultValue(encodedValue); 248 } 249 }; 250 251 private static final Function<Field, EncodedValue> GET_INITIAL_VALUE = new Function<Field, EncodedValue>() { 252 @Override 253 public EncodedValue apply(Field input) { 254 EncodedValue initialValue = input.getInitialValue(); 255 if (initialValue == null) { 256 return ImmutableEncodedValueFactory.defaultValueForType(input.getType()); 257 } 258 return initialValue; 259 } 260 }; 261 262 @Nullable @Override public Collection<? extends EncodedValue> getStaticInitializers( 263 @Nonnull PoolClassDef classDef) { 264 final SortedSet<Field> sortedStaticFields = classDef.getStaticFields(); 265 266 final int lastIndex = CollectionUtils.lastIndexOf(sortedStaticFields, HAS_INITIALIZER); 267 if (lastIndex > -1) { 268 return new AbstractCollection<EncodedValue>() { 269 @Nonnull @Override public Iterator<EncodedValue> iterator() { 270 Iterable<Field> fields = Iterables.limit(sortedStaticFields, lastIndex + 1); 271 return Iterables.transform(fields, GET_INITIAL_VALUE).iterator(); 272 } 273 274 @Override public int size() { 275 return lastIndex+1; 276 } 277 }; 278 } 279 return null; 280 } 281 282 @Nonnull @Override public Collection<? extends Field> getSortedStaticFields(@Nonnull PoolClassDef classDef) { 283 return classDef.getStaticFields(); 284 } 285 286 @Nonnull @Override public Collection<? extends Field> getSortedInstanceFields(@Nonnull PoolClassDef classDef) { 287 return classDef.getInstanceFields(); 288 } 289 290 @Nonnull @Override public Collection<? extends Field> getSortedFields(@Nonnull PoolClassDef classDef) { 291 return classDef.getFields(); 292 } 293 294 @Nonnull @Override public Collection<PoolMethod> getSortedDirectMethods(@Nonnull PoolClassDef classDef) { 295 return classDef.getDirectMethods(); 296 } 297 298 @Nonnull @Override public Collection<PoolMethod> getSortedVirtualMethods(@Nonnull PoolClassDef classDef) { 299 return classDef.getVirtualMethods(); 300 } 301 302 @Nonnull @Override public Collection<? extends PoolMethod> getSortedMethods(@Nonnull PoolClassDef classDef) { 303 return classDef.getMethods(); 304 } 305 306 @Override public int getFieldAccessFlags(@Nonnull Field field) { 307 return field.getAccessFlags(); 308 } 309 310 @Override public int getMethodAccessFlags(@Nonnull PoolMethod method) { 311 return method.getAccessFlags(); 312 } 313 314 @Nullable @Override public Set<? extends Annotation> getClassAnnotations(@Nonnull PoolClassDef classDef) { 315 Set<? extends Annotation> annotations = classDef.getAnnotations(); 316 if (annotations.size() == 0) { 317 return null; 318 } 319 return annotations; 320 } 321 322 @Nullable @Override public Set<? extends Annotation> getFieldAnnotations(@Nonnull Field field) { 323 Set<? extends Annotation> annotations = field.getAnnotations(); 324 if (annotations.size() == 0) { 325 return null; 326 } 327 return annotations; 328 } 329 330 @Nullable @Override public Set<? extends Annotation> getMethodAnnotations(@Nonnull PoolMethod method) { 331 Set<? extends Annotation> annotations = method.getAnnotations(); 332 if (annotations.size() == 0) { 333 return null; 334 } 335 return annotations; 336 } 337 338 private static final Predicate<MethodParameter> HAS_PARAMETER_ANNOTATIONS = new Predicate<MethodParameter>() { 339 @Override 340 public boolean apply(MethodParameter input) { 341 return input.getAnnotations().size() > 0; 342 } 343 }; 344 345 private static final Function<MethodParameter, Set<? extends Annotation>> PARAMETER_ANNOTATIONS = 346 new Function<MethodParameter, Set<? extends Annotation>>() { 347 @Override 348 public Set<? extends Annotation> apply(MethodParameter input) { 349 return input.getAnnotations(); 350 } 351 }; 352 353 @Nullable @Override public List<? extends Set<? extends Annotation>> getParameterAnnotations( 354 @Nonnull final PoolMethod method) { 355 final List<? extends MethodParameter> parameters = method.getParameters(); 356 boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS); 357 358 if (hasParameterAnnotations) { 359 return new AbstractForwardSequentialList<Set<? extends Annotation>>() { 360 @Nonnull @Override public Iterator<Set<? extends Annotation>> iterator() { 361 return Iterables.transform(parameters, PARAMETER_ANNOTATIONS).iterator(); 362 } 363 364 @Override public int size() { 365 return parameters.size(); 366 } 367 }; 368 } 369 return null; 370 } 371 372 @Nullable @Override public Iterable<? extends DebugItem> getDebugItems(@Nonnull PoolMethod method) { 373 MethodImplementation impl = method.getImplementation(); 374 if (impl != null) { 375 return impl.getDebugItems(); 376 } 377 return null; 378 } 379 380 @Nullable @Override public Iterable<CharSequence> getParameterNames(@Nonnull PoolMethod method) { 381 return Iterables.transform(method.getParameters(), new Function<MethodParameter, CharSequence>() { 382 @Nullable @Override public CharSequence apply(MethodParameter input) { 383 return input.getName(); 384 } 385 }); 386 } 387 388 @Override public int getRegisterCount(@Nonnull PoolMethod method) { 389 MethodImplementation impl = method.getImplementation(); 390 if (impl != null) { 391 return impl.getRegisterCount(); 392 } 393 return 0; 394 } 395 396 @Nullable @Override public Iterable<? extends Instruction> getInstructions(@Nonnull PoolMethod method) { 397 MethodImplementation impl = method.getImplementation(); 398 if (impl != null) { 399 return impl.getInstructions(); 400 } 401 return null; 402 } 403 404 @Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks( 405 @Nonnull PoolMethod method) { 406 MethodImplementation impl = method.getImplementation(); 407 if (impl != null) { 408 return impl.getTryBlocks(); 409 } 410 return ImmutableList.of(); 411 } 412 413 @Nullable @Override public CharSequence getExceptionType(@Nonnull ExceptionHandler handler) { 414 return handler.getExceptionType(); 415 } 416 417 @Nonnull @Override 418 public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull PoolMethod poolMethod) { 419 return new MutableMethodImplementation(poolMethod.getImplementation()); 420 } 421 422 @Override public void setEncodedArrayOffset(@Nonnull PoolClassDef classDef, int offset) { 423 classDef.encodedArrayOffset = offset; 424 } 425 426 @Override public int getEncodedArrayOffset(@Nonnull PoolClassDef classDef) { 427 return classDef.encodedArrayOffset; 428 } 429 430 @Override public void setAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef, int offset) { 431 classDef.annotationDirectoryOffset = offset; 432 } 433 434 @Override public int getAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef) { 435 return classDef.annotationDirectoryOffset; 436 } 437 438 @Override public void setAnnotationSetRefListOffset(@Nonnull PoolMethod method, int offset) { 439 method.annotationSetRefListOffset = offset; 440 441 } 442 @Override public int getAnnotationSetRefListOffset(@Nonnull PoolMethod method) { 443 return method.annotationSetRefListOffset; 444 } 445 446 @Override public void setCodeItemOffset(@Nonnull PoolMethod method, int offset) { 447 method.codeItemOffset = offset; 448 } 449 450 @Override public int getCodeItemOffset(@Nonnull PoolMethod method) { 451 return method.codeItemOffset; 452 } 453 454 @Override public void writeDebugItem(@Nonnull DebugWriter<CharSequence, CharSequence> writer, 455 DebugItem debugItem) throws IOException { 456 switch (debugItem.getDebugItemType()) { 457 case DebugItemType.START_LOCAL: { 458 StartLocal startLocal = (StartLocal)debugItem; 459 writer.writeStartLocal(startLocal.getCodeAddress(), 460 startLocal.getRegister(), 461 startLocal.getName(), 462 startLocal.getType(), 463 startLocal.getSignature()); 464 break; 465 } 466 case DebugItemType.END_LOCAL: { 467 EndLocal endLocal = (EndLocal)debugItem; 468 writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister()); 469 break; 470 } 471 case DebugItemType.RESTART_LOCAL: { 472 RestartLocal restartLocal = (RestartLocal)debugItem; 473 writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister()); 474 break; 475 } 476 case DebugItemType.PROLOGUE_END: { 477 writer.writePrologueEnd(debugItem.getCodeAddress()); 478 break; 479 } 480 case DebugItemType.EPILOGUE_BEGIN: { 481 writer.writeEpilogueBegin(debugItem.getCodeAddress()); 482 break; 483 } 484 case DebugItemType.LINE_NUMBER: { 485 LineNumber lineNumber = (LineNumber)debugItem; 486 writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber()); 487 break; 488 } 489 case DebugItemType.SET_SOURCE_FILE: { 490 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 491 writer.writeSetSourceFile(setSourceFile.getCodeAddress(), setSourceFile.getSourceFile()); 492 } 493 default: 494 throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); 495 } 496 } 497 498 @Override public int getItemIndex(@Nonnull PoolClassDef classDef) { 499 return classDef.classDefIndex; 500 } 501 502 @Nonnull @Override public Collection<? extends Map.Entry<PoolClassDef, Integer>> getItems() { 503 class MapEntry implements Map.Entry<PoolClassDef, Integer> { 504 @Nonnull private final PoolClassDef classDef; 505 506 public MapEntry(@Nonnull PoolClassDef classDef) { 507 this.classDef = classDef; 508 } 509 510 @Override public PoolClassDef getKey() { 511 return classDef; 512 } 513 514 @Override public Integer getValue() { 515 return classDef.classDefIndex; 516 } 517 518 @Override public Integer setValue(Integer value) { 519 int prev = classDef.classDefIndex; 520 classDef.classDefIndex = value; 521 return prev; 522 } 523 } 524 525 return new AbstractCollection<Entry<PoolClassDef, Integer>>() { 526 @Nonnull @Override public Iterator<Entry<PoolClassDef, Integer>> iterator() { 527 return new Iterator<Entry<PoolClassDef, Integer>>() { 528 Iterator<PoolClassDef> iter = internedItems.values().iterator(); 529 530 @Override public boolean hasNext() { 531 return iter.hasNext(); 532 } 533 534 @Override public Entry<PoolClassDef, Integer> next() { 535 return new MapEntry(iter.next()); 536 } 537 538 @Override public void remove() { 539 throw new UnsupportedOperationException(); 540 } 541 }; 542 } 543 544 @Override public int size() { 545 return internedItems.size(); 546 } 547 }; 548 } 549} 550