package com.hubspot.jinjava.lib.fn; import static com.hubspot.jinjava.util.Logging.ENGINE_LOG; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.bytecode.AccessFlag; import com.google.common.base.Throwables; public class InjectedContextFunctionProxy { public static ELFunctionDefinition defineProxy(String namespace, String name, Method m, Object injectedInstance) { try { ClassPool pool = ClassPool.getDefault(); String ccName = InjectedContextFunctionProxy.class.getSimpleName() + "$$" + namespace + "$$" + name; Class<?> injectedClass = null; try { injectedClass = InjectedContextFunctionProxy.class.getClassLoader().loadClass(ccName); } catch (ClassNotFoundException e) { CtClass cc = pool.makeClass(ccName); CtClass mc = pool.get(m.getDeclaringClass().getName()); CtField injectedField = CtField.make(String.format("public static %s injectedField;", m.getDeclaringClass().getName()), cc); cc.addField(injectedField); CtField injectedMethod = CtField.make(String.format("public static %s delegate;", Method.class.getName()), cc); cc.addField(injectedMethod); CtMethod ctMethod = mc.getDeclaredMethod(m.getName()); CtMethod invokeMethod = CtNewMethod.make(Modifier.PUBLIC | Modifier.STATIC, ctMethod.getReturnType(), "invoke", ctMethod.getParameterTypes(), ctMethod.getExceptionTypes(), null, cc); invokeMethod.setBody("{ return $proceed($$); }", "injectedField", m.getName()); for (CtClass param : ctMethod.getParameterTypes()) { if (param.isArray()) { invokeMethod.setModifiers(invokeMethod.getModifiers() | AccessFlag.VARARGS); break; } } cc.addMethod(invokeMethod); injectedClass = cc.toClass(); cc.detach(); } injectedClass.getField("injectedField").set(null, injectedInstance); injectedClass.getField("delegate").set(null, m); Method staticMethod = null; for (Method m1 : injectedClass.getMethods()) { if (m1.getName().equals("invoke")) { staticMethod = m1; break; } } return new ELFunctionDefinition(namespace, name, staticMethod); } catch (Throwable e) { ENGINE_LOG.error("Error creating injected context function", e); throw Throwables.propagate(e); } } }