package com.strobel.assembler.metadata; import org.apache.commons.collections4.map.LRUMap; import com.strobel.core.VerifyArgument; /** * Keeps a set of types which failed to load and also uses an LRUCache to reduce the memory footprint for cached lookups. * * @author <a href="mailto:ozizka@redhat.com">Ondrej Zizka</a> */ public final class NoRetryMetadataSystem extends MetadataSystem { private final static TypeDefinition[] PRIMITIVE_TYPES_BY_NAME = new TypeDefinition['Z' - 'B' + 1]; private final static TypeDefinition[] PRIMITIVE_TYPES_BY_DESCRIPTOR = new TypeDefinition[16]; private final LRUMap<String, Boolean> failedTypes = new LRUMap<>(1000); private final LRUMap<String, TypeDefinition> resolvedTypes = new LRUMap<>(1000); private final ITypeLoader typeLoader; public NoRetryMetadataSystem(final ITypeLoader typeLoader) { super(typeLoader); this.typeLoader = typeLoader; } public void addTypeDefinition(final TypeDefinition type) { VerifyArgument.notNull(type, "type"); resolvedTypes.put(type.getInternalName(), type); } @Override protected TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive) { if (failedTypes.containsKey(descriptor)) return null; TypeDefinition result = resolveTypeInternal(descriptor, mightBePrimitive); if (result == null) failedTypes.put(descriptor, true); return result; } protected TypeDefinition resolveTypeInternal(final String descriptor, final boolean mightBePrimitive) { VerifyArgument.notNull(descriptor, "descriptor"); if (mightBePrimitive) { if (descriptor.length() == 1) { final int primitiveHash = descriptor.charAt(0) - 'B'; if (primitiveHash >= 0 && primitiveHash < PRIMITIVE_TYPES_BY_DESCRIPTOR.length) { final TypeDefinition primitiveType = PRIMITIVE_TYPES_BY_DESCRIPTOR[primitiveHash]; if (primitiveType != null) { return primitiveType; } } } else { final int primitiveHash = hashPrimitiveName(descriptor); if (primitiveHash >= 0 && primitiveHash < PRIMITIVE_TYPES_BY_NAME.length) { final TypeDefinition primitiveType = PRIMITIVE_TYPES_BY_NAME[primitiveHash]; if (primitiveType != null && descriptor.equals(primitiveType.getName())) { return primitiveType; } } } } TypeDefinition cachedDefinition = resolvedTypes.get(descriptor); if (cachedDefinition != null) { return cachedDefinition; } final Buffer buffer = new Buffer(0); if (!this.typeLoader.tryLoadType(descriptor, buffer)) { return null; } final TypeDefinition typeDefinition = ClassFileReader.readClass(ClassFileReader.OPTIONS_DEFAULT, this, buffer); cachedDefinition = resolvedTypes.put(descriptor, typeDefinition); typeDefinition.setTypeLoader(this.typeLoader); if (cachedDefinition != null) { return cachedDefinition; } return typeDefinition; } private static int hashPrimitiveName(final String name) { if (name.length() < 3) { return 0; } return (name.charAt(0) + name.charAt(2)) % 16; } }