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 implements ClassSection<CharSequence, CharSequence, 62 TypeListPool.Key<? extends Collection<? extends CharSequence>>, PoolClassDef, Field, PoolMethod, 63 Set<? extends Annotation>, EncodedValue> { 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.getMethodDescriptor(method, true); 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<List<String>> getInterfaces(@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 Iterable<Field> fields = Iterables.limit(sortedStaticFields, lastIndex + 1); 289 return Iterables.transform(fields, 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 List<? extends MethodParameter> parameters = method.getParameters(); 374 boolean hasParameterAnnotations = Iterables.any(parameters, HAS_PARAMETER_ANNOTATIONS); 375 376 if (hasParameterAnnotations) { 377 return new AbstractForwardSequentialList<Set<? extends Annotation>>() { 378 @Nonnull @Override public Iterator<Set<? extends Annotation>> iterator() { 379 return Iterables.transform(parameters, PARAMETER_ANNOTATIONS).iterator(); 380 } 381 382 @Override public int size() { 383 return parameters.size(); 384 } 385 }; 386 } 387 return null; 388 } 389 390 @Nullable @Override public Iterable<? extends DebugItem> getDebugItems(@Nonnull PoolMethod method) { 391 MethodImplementation impl = method.getImplementation(); 392 if (impl != null) { 393 return impl.getDebugItems(); 394 } 395 return null; 396 } 397 398 @Nullable @Override public Iterable<CharSequence> getParameterNames(@Nonnull PoolMethod method) { 399 return Iterables.transform(method.getParameters(), new Function<MethodParameter, CharSequence>() { 400 @Nullable @Override public CharSequence apply(MethodParameter input) { 401 return input.getName(); 402 } 403 }); 404 } 405 406 @Override public int getRegisterCount(@Nonnull PoolMethod method) { 407 MethodImplementation impl = method.getImplementation(); 408 if (impl != null) { 409 return impl.getRegisterCount(); 410 } 411 return 0; 412 } 413 414 @Nullable @Override public Iterable<? extends Instruction> getInstructions(@Nonnull PoolMethod method) { 415 MethodImplementation impl = method.getImplementation(); 416 if (impl != null) { 417 return impl.getInstructions(); 418 } 419 return null; 420 } 421 422 @Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks( 423 @Nonnull PoolMethod method) { 424 MethodImplementation impl = method.getImplementation(); 425 if (impl != null) { 426 return impl.getTryBlocks(); 427 } 428 return ImmutableList.of(); 429 } 430 431 @Nullable @Override public CharSequence getExceptionType(@Nonnull ExceptionHandler handler) { 432 return handler.getExceptionType(); 433 } 434 435 @Nonnull @Override 436 public MutableMethodImplementation makeMutableMethodImplementation(@Nonnull PoolMethod poolMethod) { 437 return new MutableMethodImplementation(poolMethod.getImplementation()); 438 } 439 440 @Override public void setEncodedArrayOffset(@Nonnull PoolClassDef classDef, int offset) { 441 classDef.encodedArrayOffset = offset; 442 } 443 444 @Override public int getEncodedArrayOffset(@Nonnull PoolClassDef classDef) { 445 return classDef.encodedArrayOffset; 446 } 447 448 @Override public void setAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef, int offset) { 449 classDef.annotationDirectoryOffset = offset; 450 } 451 452 @Override public int getAnnotationDirectoryOffset(@Nonnull PoolClassDef classDef) { 453 return classDef.annotationDirectoryOffset; 454 } 455 456 @Override public void setAnnotationSetRefListOffset(@Nonnull PoolMethod method, int offset) { 457 method.annotationSetRefListOffset = offset; 458 459 } 460 @Override public int getAnnotationSetRefListOffset(@Nonnull PoolMethod method) { 461 return method.annotationSetRefListOffset; 462 } 463 464 @Override public void setCodeItemOffset(@Nonnull PoolMethod method, int offset) { 465 method.codeItemOffset = offset; 466 } 467 468 @Override public int getCodeItemOffset(@Nonnull PoolMethod method) { 469 return method.codeItemOffset; 470 } 471 472 @Override public void writeDebugItem(@Nonnull DebugWriter<CharSequence, CharSequence> writer, 473 DebugItem debugItem) throws IOException { 474 switch (debugItem.getDebugItemType()) { 475 case DebugItemType.START_LOCAL: { 476 StartLocal startLocal = (StartLocal)debugItem; 477 writer.writeStartLocal(startLocal.getCodeAddress(), 478 startLocal.getRegister(), 479 startLocal.getName(), 480 startLocal.getType(), 481 startLocal.getSignature()); 482 break; 483 } 484 case DebugItemType.END_LOCAL: { 485 EndLocal endLocal = (EndLocal)debugItem; 486 writer.writeEndLocal(endLocal.getCodeAddress(), endLocal.getRegister()); 487 break; 488 } 489 case DebugItemType.RESTART_LOCAL: { 490 RestartLocal restartLocal = (RestartLocal)debugItem; 491 writer.writeRestartLocal(restartLocal.getCodeAddress(), restartLocal.getRegister()); 492 break; 493 } 494 case DebugItemType.PROLOGUE_END: { 495 writer.writePrologueEnd(debugItem.getCodeAddress()); 496 break; 497 } 498 case DebugItemType.EPILOGUE_BEGIN: { 499 writer.writeEpilogueBegin(debugItem.getCodeAddress()); 500 break; 501 } 502 case DebugItemType.LINE_NUMBER: { 503 LineNumber lineNumber = (LineNumber)debugItem; 504 writer.writeLineNumber(lineNumber.getCodeAddress(), lineNumber.getLineNumber()); 505 break; 506 } 507 case DebugItemType.SET_SOURCE_FILE: { 508 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 509 writer.writeSetSourceFile(setSourceFile.getCodeAddress(), setSourceFile.getSourceFile()); 510 } 511 default: 512 throw new ExceptionWithContext("Unexpected debug item type: %d", debugItem.getDebugItemType()); 513 } 514 } 515 516 @Override public int getItemIndex(@Nonnull PoolClassDef classDef) { 517 return classDef.classDefIndex; 518 } 519 520 @Nonnull @Override public Collection<? extends Map.Entry<PoolClassDef, Integer>> getItems() { 521 class MapEntry implements Map.Entry<PoolClassDef, Integer> { 522 @Nonnull private final PoolClassDef classDef; 523 524 public MapEntry(@Nonnull PoolClassDef classDef) { 525 this.classDef = classDef; 526 } 527 528 @Override public PoolClassDef getKey() { 529 return classDef; 530 } 531 532 @Override public Integer getValue() { 533 return classDef.classDefIndex; 534 } 535 536 @Override public Integer setValue(Integer value) { 537 int prev = classDef.classDefIndex; 538 classDef.classDefIndex = value; 539 return prev; 540 } 541 } 542 543 return new AbstractCollection<Entry<PoolClassDef, Integer>>() { 544 @Nonnull @Override public Iterator<Entry<PoolClassDef, Integer>> iterator() { 545 return new Iterator<Entry<PoolClassDef, Integer>>() { 546 Iterator<PoolClassDef> iter = internedItems.values().iterator(); 547 548 @Override public boolean hasNext() { 549 return iter.hasNext(); 550 } 551 552 @Override public Entry<PoolClassDef, Integer> next() { 553 return new MapEntry(iter.next()); 554 } 555 556 @Override public void remove() { 557 throw new UnsupportedOperationException(); 558 } 559 }; 560 } 561 562 @Override public int size() { 563 return internedItems.size(); 564 } 565 }; 566 } 567} 568