/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package com.db4o.ta.instrumentation;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.reflect.*;
import com.db4o.activation.*;
import com.db4o.instrumentation.core.*;
import com.db4o.instrumentation.util.*;
import com.db4o.ta.*;
/**
* @exclude
*/
class InjectTAInfrastructureEdit implements BloatClassEdit {
private final LocalVariable THIS_VAR = new LocalVariable(0);
private final ClassFilter _instrumentedClassesFilter;
public InjectTAInfrastructureEdit(ClassFilter instrumentedClassesFilter) {
_instrumentedClassesFilter = instrumentedClassesFilter;
}
public InstrumentationStatus enhance(ClassEditor ce, ClassLoader origLoader, BloatLoaderContext loaderContext) {
if(isActivatableItself(ce)) {
return InstrumentationStatus.FAILED;
}
if(isAlreadyInstrumented(ce, loaderContext)) {
return InstrumentationStatus.NOT_INSTRUMENTED;
}
try {
Class clazz = BloatUtil.classForEditor(ce, origLoader);
Class activatableClazz = origLoader.loadClass(Activatable.class.getName());
if(BloatUtil.implementsInHierarchy(ce, activatableClazz, loaderContext)) {
return InstrumentationStatus.NOT_INSTRUMENTED;
}
if(!_instrumentedClassesFilter.accept(clazz)) {
return InstrumentationStatus.NOT_INSTRUMENTED;
}
String superClassName = BloatUtil.normalizeClassName(ce.superclass());
Class superClazz = origLoader.loadClass(superClassName);
if(!(_instrumentedClassesFilter.accept(superClazz))) {
ce.addInterface(Activatable.class);
createActivatorField(ce);
createBindMethod(ce);
createActivateMethod(ce);
ce.commit();
return InstrumentationStatus.INSTRUMENTED;
}
return InstrumentationStatus.NOT_INSTRUMENTED;
} catch (ClassNotFoundException exc) {
return InstrumentationStatus.FAILED;
}
}
@decaf.ReplaceFirst(value="return false;", platforms={decaf.Platform.JDK11, decaf.Platform.JDK12})
private boolean isEnum(Class clazz) {
return clazz.isEnum();
}
private boolean isActivatableItself(ClassEditor ce) {
return BloatUtil.implementsDirectly(ce, Activatable.class);
}
private boolean isAlreadyInstrumented(ClassEditor ce, BloatLoaderContext context) {
try {
return BloatUtil.implementsInHierarchy(ce, Activatable.class, context);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
}
}
private void createActivatorField(ClassEditor ce) {
// private transient Activator _activator;
FieldEditor fieldEditor = new FieldEditor(ce, Modifiers.PRIVATE | Modifiers.TRANSIENT, Type.getType(Activator.class), TransparentActivationInstrumentationConstants.ACTIVATOR_FIELD_NAME);
fieldEditor.commit();
}
private void createBindMethod(ClassEditor ce) {
// public void bind(Activator activator)
final Type activatorType = Type.getType(Activator.class);
String methodName = TransparentActivationInstrumentationConstants.BIND_METHOD_NAME;
Type[] paramTypes = { activatorType };
MethodEditor methodEditor = new MethodEditor(ce, Modifiers.PUBLIC, Type.VOID, methodName, paramTypes, new Type[] {});
LabelGenerator labelGen = new LabelGenerator();
Label startLabel = labelGen.createLabel(true);
Label differentActivatorLabel = labelGen.createLabel(true);
Label setActivatorLabel = labelGen.createLabel(true);
LocalVariable activatorArg = new LocalVariable(1);
methodEditor.addLabel(startLabel);
// if (_activator == activator) {
// return;
// }
loadActivatorFieldOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_aload, activatorArg);
methodEditor.addInstruction(Opcode.opc_if_acmpne, differentActivatorLabel);
methodEditor.addInstruction(Opcode.opc_return);
// if (activator != null && _activator != null) {
// throw new IllegalStateException();
// }
methodEditor.addLabel(differentActivatorLabel);
methodEditor.addInstruction(Opcode.opc_aload, activatorArg);
methodEditor.addInstruction(Opcode.opc_ifnull, setActivatorLabel);
loadActivatorFieldOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_ifnull, setActivatorLabel);
throwException(methodEditor, IllegalStateException.class);
// _activator = activator;
methodEditor.addLabel(setActivatorLabel);
loadThisOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_aload, activatorArg);
methodEditor.addInstruction(Opcode.opc_putfield, createFieldReference(ce.type(), TransparentActivationInstrumentationConstants.ACTIVATOR_FIELD_NAME, activatorType));
methodEditor.addInstruction(Opcode.opc_return);
methodEditor.commit();
}
private void throwException(MethodEditor methodEditor, Class exceptionType) {
Type illegalStateExceptionType = Type.getType(exceptionType);
methodEditor.addInstruction(Opcode.opc_new, illegalStateExceptionType);
methodEditor.addInstruction(Opcode.opc_dup);
methodEditor.addInstruction(Opcode.opc_invokespecial, createMethodReference(illegalStateExceptionType, TransparentActivationInstrumentationConstants.INIT_METHOD_NAME, new Type[0], Type.VOID));
methodEditor.addInstruction(Opcode.opcx_athrow);
}
private void createActivateMethod(ClassEditor ce) {
// protected void activate()
final Type activationPurpose = Type.getType(ActivationPurpose.class);
final Type activatorType = Type.getType(Activator.class);
String methodName = TransparentActivationInstrumentationConstants.ACTIVATE_METHOD_NAME;
MethodEditor methodEditor = new MethodEditor(ce, Modifiers.PUBLIC, Type.VOID, methodName, new Type[] { activationPurpose }, new Type[] {});
LabelGenerator labelGen = new LabelGenerator();
Label startLabel = labelGen.createLabel(true);
Label activateLabel = labelGen.createLabel(true);
// if (_activator == null) { return; }
methodEditor.addLabel(startLabel);
loadActivatorFieldOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_ifnonnull, activateLabel);
methodEditor.addInstruction(Opcode.opc_return);
// _activator.activateForRead();
methodEditor.addLabel(activateLabel);
loadActivatorFieldOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_aload, new LocalVariable(1));
methodEditor.addInstruction(Opcode.opc_invokeinterface, createMethodReference(activatorType, TransparentActivationInstrumentationConstants.ACTIVATOR_ACTIVATE_METHOD_NAME, new Type[] { activationPurpose }, Type.VOID));
methodEditor.addInstruction(Opcode.opc_return);
methodEditor.commit();
}
private void loadThisOnStack(MethodEditor methodEditor) {
methodEditor.addInstruction(Opcode.opc_aload, THIS_VAR);
}
private void loadActivatorFieldOnStack(MethodEditor methodEditor) {
Type activatorType = Type.getType(Activator.class);
loadThisOnStack(methodEditor);
methodEditor.addInstruction(Opcode.opc_getfield, createFieldReference(methodEditor.declaringClass().type(), TransparentActivationInstrumentationConstants.ACTIVATOR_FIELD_NAME, activatorType));
}
private MemberRef createMethodReference(Type parent, String name, Type[] args, Type ret) {
return createMemberRef(parent, name, Type.getType(args, ret));
}
private MemberRef createMemberRef(Type parent, String name, Type type) {
NameAndType nameAndType = new NameAndType(name, type);
return new MemberRef(parent, nameAndType);
}
private MemberRef createFieldReference(Type parent, String name, Type type) {
return createMemberRef(parent, name, type);
}
}