| /* |
| * Copyright (c) 2016 Mockito contributors |
| * This program is made available under the terms of the MIT License. |
| */ |
| package org.mockito.internal.creation.bytebuddy; |
| |
| import net.bytebuddy.TypeCache; |
| import org.mockito.mock.SerializableMode; |
| |
| import java.lang.ref.ReferenceQueue; |
| import java.util.Set; |
| import java.util.concurrent.Callable; |
| |
| class TypeCachingBytecodeGenerator extends ReferenceQueue<ClassLoader> implements BytecodeGenerator { |
| |
| private final Object BOOTSTRAP_LOCK = new Object(); |
| |
| private final BytecodeGenerator bytecodeGenerator; |
| |
| private final TypeCache<MockitoMockKey> typeCache; |
| |
| public TypeCachingBytecodeGenerator(BytecodeGenerator bytecodeGenerator, boolean weak) { |
| this.bytecodeGenerator = bytecodeGenerator; |
| typeCache = new TypeCache.WithInlineExpunction<MockitoMockKey>(weak ? TypeCache.Sort.WEAK : TypeCache.Sort.SOFT); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> Class<T> mockClass(final MockFeatures<T> params) { |
| try { |
| ClassLoader classLoader = params.mockedType.getClassLoader(); |
| return (Class<T>) typeCache.findOrInsert(classLoader, |
| new MockitoMockKey(params.mockedType, params.interfaces, params.serializableMode, params.stripAnnotations), |
| new Callable<Class<?>>() { |
| @Override |
| public Class<?> call() throws Exception { |
| return bytecodeGenerator.mockClass(params); |
| } |
| }, BOOTSTRAP_LOCK); |
| } catch (IllegalArgumentException exception) { |
| Throwable cause = exception.getCause(); |
| if (cause instanceof RuntimeException) { |
| throw (RuntimeException) cause; |
| } else { |
| throw exception; |
| } |
| } |
| } |
| |
| private static class MockitoMockKey extends TypeCache.SimpleKey { |
| |
| private final SerializableMode serializableMode; |
| private final boolean stripAnnotations; |
| |
| private MockitoMockKey(Class<?> type, |
| Set<Class<?>> additionalType, |
| SerializableMode serializableMode, |
| boolean stripAnnotations) { |
| super(type, additionalType); |
| this.serializableMode = serializableMode; |
| this.stripAnnotations = stripAnnotations; |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (this == object) return true; |
| if (object == null || getClass() != object.getClass()) return false; |
| if (!super.equals(object)) return false; |
| MockitoMockKey that = (MockitoMockKey) object; |
| return stripAnnotations == that.stripAnnotations |
| && serializableMode.equals(that.serializableMode); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + (stripAnnotations ? 1 : 0); |
| result = 31 * result + serializableMode.hashCode(); |
| return result; |
| } |
| } |
| } |