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