/*******************************************************************************
* Copyright (c) 2010 Fraunhofer IWU and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.composition.cache.behaviours;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldNode;
import net.enilink.composition.asm.AsmUtils;
import net.enilink.composition.asm.BehaviourClassNode;
import net.enilink.composition.asm.BehaviourMethodProcessor;
import net.enilink.composition.asm.DependsOn;
import net.enilink.composition.asm.ExtendedMethod;
import net.enilink.composition.asm.Types;
import net.enilink.composition.asm.util.BehaviourMethodGenerator;
import net.enilink.composition.cache.IPropertyCache;
import net.enilink.composition.cache.annotations.Cacheable;
import com.google.inject.Inject;
@DependsOn(BehaviourMethodProcessor.class)
public class CacheBehaviourMethodProcessor implements BehaviourMethodProcessor,
Opcodes, Types {
@Override
public boolean appliesTo(BehaviourClassNode classNode, ExtendedMethod method) {
return AsmUtils.findAnnotation(Cacheable.class, method
.getOverriddenMethod()) != null
&& !method.getOverriddenMethod().getReturnType().equals(
Void.TYPE);
}
@Override
public boolean implementsMethod(Class<?> targetClass, Method method) {
return !Modifier.isAbstract(method.getModifiers())
&& AsmUtils.findAnnotation(Cacheable.class, method) != null
&& !method.getReturnType().equals(Void.TYPE);
}
@SuppressWarnings("unchecked")
@Override
public void initialize(BehaviourClassNode classNode) throws Exception {
FieldNode cacheField = new FieldNode(Opcodes.ACC_PRIVATE, "cache", Type
.getDescriptor(IPropertyCache.class), null, null);
cacheField.visitAnnotation(Type.getDescriptor(Inject.class), true);
classNode.fields.add(cacheField);
}
@Override
public void process(BehaviourClassNode classNode, ExtendedMethod method)
throws Exception {
boolean isAbstract = method.instructions.size() == 0;
Type returnType = Type.getReturnType(method.desc);
Cacheable cacheable = AsmUtils.findAnnotation(Cacheable.class, method
.getOverriddenMethod());
String cacheKey = cacheable.key().isEmpty() ? method.name : cacheable
.key();
BehaviourMethodGenerator gen = new BehaviourMethodGenerator(method);
if (!isAbstract) {
gen.pushInsns();
}
gen.loadThis();
gen.getField("cache", Type.getType(IPropertyCache.class));
gen.loadBean();
gen.push(cacheKey);
gen.loadArgArray();
gen.invokeInterface(Type.getType(IPropertyCache.class),
new org.objectweb.asm.commons.Method("get", OBJECT_TYPE,
new Type[] { OBJECT_TYPE, OBJECT_TYPE,
Type.getType(Object[].class) }));
gen.dup();
Label isNull = gen.newLabel();
gen.ifNull(isNull);
gen.unbox(returnType);
gen.returnValue();
gen.mark(isNull);
gen.pop();
if (!isAbstract) {
gen.peekInsns().insertBefore(gen.peekInsns().getFirst(),
gen.instructions);
gen.instructions.clear();
}
int result = gen.newLocal(returnType);
if (!isAbstract) {
// add caching instructions
for (AbstractInsnNode insn : method.instructions.toArray()) {
if (insn.getOpcode() >= IRETURN & insn.getOpcode() <= ARETURN) {
gen.pushInsns();
storeValue(result, returnType, cacheKey, gen);
gen.peekInsns().insertBefore(insn, gen.instructions);
gen.instructions.clear();
}
}
} else {
// generate super call and store value in cache
gen.loadThis();
gen.loadArgs();
gen.invokeSpecial(classNode.getParentType(),
new org.objectweb.asm.commons.Method(method.name,
method.desc));
storeValue(result, returnType, cacheKey, gen);
gen.returnValue();
}
}
private void storeValue(int resultVar, Type type, String cacheKey,
BehaviourMethodGenerator gen) {
gen.storeLocal(resultVar);
gen.loadThis();
gen.getField("cache", Type.getType(IPropertyCache.class));
gen.loadBean();
gen.push(cacheKey);
gen.loadArgArray();
gen.loadLocal(resultVar);
gen.box(type);
gen.invokeInterface(Type.getType(IPropertyCache.class),
new org.objectweb.asm.commons.Method("put", OBJECT_TYPE,
new Type[] { OBJECT_TYPE, OBJECT_TYPE,
Type.getType(Object[].class), OBJECT_TYPE }));
gen.unbox(type);
}
}