/* * Copyright (c) 2006-2011 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.util.*; import mockit.internal.expectations.*; import mockit.internal.state.*; import mockit.internal.util.*; public final class MockingBridge implements InvocationHandler { public static final int RECORD_OR_REPLAY = 1; public static final int CALL_STATIC_MOCK = 3; public static final int CALL_INSTANCE_MOCK = 4; public static final int UPDATE_MOCK_STATE = 5; public static final int EXIT_REENTRANT_MOCK = 6; private static final int FIRST_TARGET_WITH_EXTRA_ARG = CALL_INSTANCE_MOCK; private static final Object[] EMPTY_ARGS = {}; @SuppressWarnings({"UnusedDeclaration"}) public static final MockingBridge MB = new MockingBridge(); public Object invoke(Object mocked, Method method, Object[] args) throws Throwable { int targetId = (Integer) args[0]; if (mocked != null && instanceOfClassThatParticipatesInClassLoading(mocked) && wasCalledDuringClassLoading()) { return targetId == UPDATE_MOCK_STATE ? false : Void.class; } int mockIndex = targetId < FIRST_TARGET_WITH_EXTRA_ARG ? -1 : (Integer) args[7]; String mockClassInternalName = (String) args[2]; if (targetId == UPDATE_MOCK_STATE) { return TestRun.updateMockState(mockClassInternalName, mockIndex); } else if (targetId == EXIT_REENTRANT_MOCK) { TestRun.exitReentrantMock(mockClassInternalName, mockIndex); return null; } String mockName = (String) args[3]; String mockDesc = (String) args[4]; String genericSignature = (String) args[5]; String exceptions = (String) args[6]; Object[] mockArgs = extractMockArguments(targetId, args); if (targetId != RECORD_OR_REPLAY) { return callMock(mocked, targetId, mockClassInternalName, mockName, mockDesc, mockIndex, mockArgs); } if (TestRun.isInsideNoMockingZone()) { return Void.class; } TestRun.enterNoMockingZone(); try { int mockAccess = (Integer) args[1]; int executionMode = (Integer) args[7]; return RecordAndReplayExecution.recordOrReplay( mocked, mockAccess, mockClassInternalName, mockName + mockDesc, genericSignature, exceptions, executionMode, mockArgs); } finally { TestRun.exitNoMockingZone(); } } private static boolean instanceOfClassThatParticipatesInClassLoading(Object mocked) { Class<?> mockedClass = mocked.getClass(); return mockedClass == File.class || mockedClass == URL.class || mockedClass == FileInputStream.class || Vector.class.isInstance(mocked) || Hashtable.class.isInstance(mocked); } private static boolean wasCalledDuringClassLoading() { StackTraceElement[] st = new Throwable().getStackTrace(); for (int i = 3; i < st.length; i++) { StackTraceElement ste = st[i]; if ("ClassLoader.java".equals(ste.getFileName()) && "loadClass".equals(ste.getMethodName())) { return true; } } return false; } private static Object[] extractMockArguments(int targetId, Object[] args) { int i = targetId > RECORD_OR_REPLAY && targetId < FIRST_TARGET_WITH_EXTRA_ARG ? 7 : 8; if (args.length > i) { Object[] mockArgs = new Object[args.length - i]; System.arraycopy(args, i, mockArgs, 0, mockArgs.length); return mockArgs; } return EMPTY_ARGS; } private static Object callMock( Object mocked, int targetId, String mockClassInternalName, String mockName, String mockDesc, int mockIndex, Object[] mockArgs) { Class<?> mockClass; Object mock; if (targetId == CALL_STATIC_MOCK) { mock = mocked; String mockClassName = getMockClassName(mockClassInternalName); mockClass = Utilities.loadClass(mockClassName); } else { assert targetId == CALL_INSTANCE_MOCK; if (mockIndex < 0) { // call to instance mock method on mock not yet instantiated String mockClassName = getMockClassName(mockClassInternalName); mock = Utilities.newInstance(mockClassName); } else { // call to instance mock method on mock already instantiated mock = TestRun.getMock(mockIndex); } mockClass = mock.getClass(); setItFieldIfAny(mockClass, mock, mocked); } Class<?>[] paramClasses = Utilities.getParameterTypes(mockDesc); Object result = Utilities.invoke(mockClass, mock, mockName, paramClasses, mockArgs); return result; } private static String getMockClassName(String mockClassInternalName) { return mockClassInternalName.replace('/', '.'); } private static void setItFieldIfAny(Class<?> mockClass, Object mock, Object mocked) { try { Field itField = mockClass.getDeclaredField("it"); Utilities.setFieldValue(itField, mock, mocked); } catch (NoSuchFieldException ignore) {} } }