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