Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
act.mail.bytecode.SenderEnhancer Maven / Gradle / Ivy
package act.mail.bytecode;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.asm.Label;
import act.asm.MethodVisitor;
import act.asm.Opcodes;
import act.asm.Type;
import act.asm.tree.*;
import act.controller.meta.HandlerParamMetaInfo;
import act.controller.meta.LocalVariableMetaInfo;
import act.mail.MailerContext;
import act.mail.meta.SenderMethodMetaInfo;
import act.util.AsmTypes;
import org.osgl.logging.L;
import org.osgl.logging.Logger;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.Future;
public class SenderEnhancer extends MethodVisitor implements Opcodes {
private static final Logger logger = L.get(SenderEnhancer.class);
private SenderMethodMetaInfo info;
private MethodVisitor next;
private int paramIdShift = 0;
public SenderEnhancer(final MethodVisitor mv, SenderMethodMetaInfo meta, final int access, final String name, final String desc, final String signature, final String[] exceptions) {
super(ASM5, new MethodNode(access, name, desc, signature, exceptions));
this.info = meta;
this.next = mv;
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
if (!"this".equals(name)) {
int paramId = index;
if (!info.isStatic()) {
paramId--;
}
paramId -= paramIdShift;
if (paramId < info.paramCount()) {
HandlerParamMetaInfo param = info.param(paramId);
param.name(name);
if (Type.getType(long.class).equals(param.type()) || Type.getType(double.class).equals(param.type())) {
paramIdShift++;
}
}
LocalVariableMetaInfo local = new LocalVariableMetaInfo(index, name, desc, start, end);
info.addLocal(local);
}
super.visitLocalVariable(name, desc, signature, start, end, index);
}
@Override
public void visitEnd() {
MethodNode mn = (MethodNode) mv;
transform(mn);
mn.accept(next);
super.visitEnd();
}
private void transform(MethodNode mn) {
new Transformer(mn, info).doIt();
}
private static class Transformer {
MethodNode mn;
InsnList instructions;
private SenderMethodMetaInfo meta;
List lblList = C.newSizedList(20);
Transformer(MethodNode mn, SenderMethodMetaInfo meta) {
this.mn = mn;
this.meta = meta;
this.instructions = mn.instructions;
}
void doIt() {
ListIterator itr = instructions.iterator();
Segment cur = null;
while (itr.hasNext()) {
AbstractInsnNode insn = itr.next();
if (insn.getType() == AbstractInsnNode.LABEL) {
cur = new Segment(((LabelNode) insn).getLabel(), meta, instructions, itr, this);
} else if (null != cur) {
if (cur.handle(insn)) {
cur = null;
}
}
}
}
private static abstract class InstructionHandler {
Segment segment;
SenderMethodMetaInfo meta;
InstructionHandler(Segment segment, SenderMethodMetaInfo meta) {
this.segment = segment;
this.meta = meta;
}
protected abstract boolean handle(AbstractInsnNode node);
protected void refreshIteratorNext() {
segment.itr.previous();
segment.itr.next();
}
}
private static class Segment {
Label startLabel;
InsnList instructions;
SenderMethodMetaInfo meta;
ListIterator itr;
Transformer trans;
private Map handlers;
Segment(Label start, SenderMethodMetaInfo meta, InsnList instructions, ListIterator itr, Transformer trans) {
E.NPE(meta);
this.startLabel = start;
this.meta = meta;
this.instructions = instructions;
this.itr = itr;
this.trans = trans;
this.handlers = C.Map(
AbstractInsnNode.METHOD_INSN, new InvocationHandler(this, meta)
);
trans.lblList.add(start);
}
protected boolean handle(AbstractInsnNode node) {
InstructionHandler handler = handlers.get(node.getType());
if (null != handler) {
return handler.handle(node);
} else {
return false;
}
}
}
private static class InvocationHandler extends InstructionHandler {
InvocationHandler(Segment segment, SenderMethodMetaInfo meta) {
super(segment, meta);
}
@Override
protected boolean handle(AbstractInsnNode node) {
MethodInsnNode n = (MethodInsnNode) node;
Type type = Type.getMethodType(n.desc);
Type retType = type.getReturnType();
//String method = n.name;
//String owner = Type.getType(n.owner).toString();
if (MailerContext.class.getName().equals(retType.getClassName())) {
return injectCreatingMailerContextCode(n);
} else if (Future.class.getName().equals(retType.getClassName()) && "send".equals(n.name)) {
return injectRenderArgSetCode(n);
}
return false;
}
private boolean injectCreatingMailerContextCode(AbstractInsnNode insnNode) {
AbstractInsnNode node = insnNode.getNext();
InsnList list = new InsnList();
if (node instanceof VarInsnNode) {
VarInsnNode varNode = (VarInsnNode) node;
if (varNode.getOpcode() == ASTORE) {
meta.appCtxLocalVariableTableIndex(varNode.var);
list.add(new TypeInsnNode(NEW, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME));
list.add(new InsnNode(DUP));
list.add(new MethodInsnNode(INVOKESTATIC, AsmTypes.APP_INTERNAL_NAME, "instance", "()" + AsmTypes.APP_DESC, false));
String confId = meta.configId();
if (null == confId) {
confId = "default";
}
list.add(new LdcInsnNode(confId));
String templateContext = meta.templateContext();
if (null != templateContext) {
list.add(new LdcInsnNode(templateContext));
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;Ljava/lang/String;)V", false));
} else {
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;)V", false));
}
segment.instructions.insertBefore(node, list);
segment.instructions.remove(insnNode);
return true;
}
return false;
} else {
list.add(new TypeInsnNode(NEW, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME));
list.add(new InsnNode(DUP));
list.add(new MethodInsnNode(INVOKESTATIC, AsmTypes.APP_INTERNAL_NAME, "instance", "()" + AsmTypes.APP_DESC, false));
String confId = meta.configId();
if (null == confId) {
confId = "default";
}
list.add(new LdcInsnNode(confId));
String templateContext = meta.templateContext();
if (null != templateContext) {
list.add(new LdcInsnNode(templateContext));
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;Ljava/lang/String;)V", false));
} else {
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;)V", false));
}
segment.instructions.insertBefore(node, list);
segment.instructions.remove(insnNode);
node = node.getNext();
while (node.getOpcode() != POP) {
node = node.getNext();
}
int maxLocal = segment.trans.mn.maxLocals;
segment.trans.mn.maxLocals++;
segment.instructions.insertBefore(node, new VarInsnNode(ASTORE, maxLocal));
segment.instructions.remove(node);
meta.appCtxLocalVariableTableIndex(maxLocal);
return true;
}
}
@SuppressWarnings("FallThrough")
private boolean injectRenderArgSetCode(AbstractInsnNode invokeNode) {
AbstractInsnNode node = invokeNode.getPrevious();
List loadInsnInfoList = new ArrayList<>();
String templateLiteral = null;
while (null != node) {
int type = node.getType();
boolean breakWhile = false;
switch (type) {
case AbstractInsnNode.LABEL:
case AbstractInsnNode.FRAME:
node = node.getNext();
breakWhile = true;
break;
case AbstractInsnNode.VAR_INSN:
VarInsnNode n = (VarInsnNode) node;
if (0 == n.var && !segment.meta.isStatic()) {
// skip "this"
break;
}
LoadInsn insn = LoadInsn.of(n.getOpcode());
if (insn.isStoreInsn()) {
break;
}
LoadInsnInfo info = new LoadInsnInfo(insn, n.var);
loadInsnInfoList.add(info);
break;
case AbstractInsnNode.LDC_INSN:
LdcInsnNode ldc = (LdcInsnNode) node;
if (null != templateLiteral) {
logger.warn("Cannot have more than one template path parameter in the render call. Template path[%s] ignored", templateLiteral);
} else if (!(ldc.cst instanceof String)) {
logger.warn("Template path must be strictly String type. Found: %s", ldc.cst);
} else {
templateLiteral = ldc.cst.toString();
}
default:
}
if (breakWhile) {
break;
}
node = node.getPrevious();
}
InsnList list = new InsnList();
int len = loadInsnInfoList.size();
// SetRenderArgs enhancement
int ctxId = meta.appCtxLocalVariableTableIndex();
if (ctxId < 0) {
list.add(new TypeInsnNode(NEW, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME));
list.add(new InsnNode(DUP));
list.add(new MethodInsnNode(INVOKESTATIC, AsmTypes.APP_INTERNAL_NAME, "instance", "()" + AsmTypes.APP_DESC, false));
String confId = meta.configId();
if (null == confId) {
confId = "default";
}
list.add(new LdcInsnNode(confId));
String templateContext = meta.templateContext();
if (null != templateContext) {
list.add(new LdcInsnNode(templateContext));
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;Ljava/lang/String;)V", false));
} else {
list.add(new MethodInsnNode(INVOKESPECIAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, "", "(Lact/app/App;Ljava/lang/String;)V", false));
}
int maxLocal = segment.trans.mn.maxLocals;
list.add(new VarInsnNode(ASTORE, maxLocal));
segment.trans.mn.maxLocals++;
ctxId = maxLocal;
}
list.add(new VarInsnNode(ALOAD, ctxId));
S.Buffer sb = S.newBuffer();
for (int i = 0; i < len; ++i) {
LoadInsnInfo info = loadInsnInfoList.get(i);
info.appendTo(list, segment, sb);
}
LdcInsnNode ldc = new LdcInsnNode(sb.toString());
list.add(ldc);
MethodInsnNode invokeRenderArg = new MethodInsnNode(INVOKEVIRTUAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, RENDER_ARG_NAMES_NM, RENDER_ARG_NAMES_DESC, false);
list.add(invokeRenderArg);
// setTemplatePath enhancement
if (null != templateLiteral) {
LdcInsnNode insnNode = new LdcInsnNode(templateLiteral);
list.add(insnNode);
MethodInsnNode invokeTemplatePath = new MethodInsnNode(INVOKEVIRTUAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, TEMPLATE_LITERAL, TEMPLATE_LITERAL_DESC, false);
list.add(invokeTemplatePath);
} else {
String className = meta.classInfo().className();
String method = meta.name();
list.add(new LdcInsnNode(className));
list.add(new LdcInsnNode(method));
list.add(new MethodInsnNode(INVOKEVIRTUAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, SENDER_PATH_NM, SENDER_LITERAL_DESC, false));
}
InsnNode pop = new InsnNode(POP);
list.add(pop);
list.add(new VarInsnNode(ALOAD, ctxId));
String method = meta.appCtxLocalVariableTableIndex() < 0 ? "doSend" : "doSendWithoutLoadThreadLocal";
MethodInsnNode realSend = new MethodInsnNode(INVOKESTATIC, "act/mail/Mailer$Util", method, "(Lact/mail/MailerContext;)Ljava/util/concurrent/Future;", false);
list.add(realSend);
segment.instructions.insertBefore(node, list);
while (node != invokeNode) {
AbstractInsnNode n0 = node.getNext();
segment.instructions.remove(node);
node = n0;
}
segment.instructions.remove(invokeNode);
return true;
}
}
private static final int _I = 'I';
private static final int _Z = 'Z';
private static final int _S = 'S';
private static final int _B = 'B';
private static final int _C = 'C';
private enum LoadInsn {
I(ILOAD) {
void appendTo(InsnList list, int varIndex, String type) {
super.appendTo(list, varIndex, type);
String owner, desc;
switch (type.hashCode()) {
case _I:
owner = "java/lang/Integer";
desc = "(I)Ljava/lang/Integer;";
break;
case _Z:
owner = "java/lang/Boolean";
desc = "(Z)Ljava/lang/Boolean;";
break;
case _S:
owner = "java/lang/Short";
desc = "(S)Ljava/lang/Short";
break;
case _B:
owner = "java/lang/Byte";
desc = "(B)Ljava/lang/Byte;";
break;
case _C:
owner = "java/lang/Character";
desc = "(C)Ljava/lang/Character;";
break;
default:
throw E.unexpected("int var type not recognized: %s", type);
}
MethodInsnNode method = new MethodInsnNode(INVOKESTATIC, owner, "valueOf", desc, false);
list.add(method);
}
}, L(LLOAD) {
@Override
void appendTo(InsnList list, int varIndex, String type) {
super.appendTo(list, varIndex, type);
MethodInsnNode method = new MethodInsnNode(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
list.add(method);
}
}, F(FLOAD) {
@Override
void appendTo(InsnList list, int varIndex, String type) {
super.appendTo(list, varIndex, type);
MethodInsnNode method = new MethodInsnNode(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
list.add(method);
}
}, D(DLOAD) {
@Override
void appendTo(InsnList list, int varIndex, String type) {
super.appendTo(list, varIndex, type);
MethodInsnNode method = new MethodInsnNode(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
list.add(method);
}
}, A(ALOAD), Store(-1) {
@Override
void appendTo(InsnList list, int varIndex, String type) {
throw E.unsupport();
}
};
private int opcode;
LoadInsn(int opcode) {
this.opcode = opcode;
}
static LoadInsn of(int opcode) {
switch (opcode) {
case ILOAD:
return I;
case LLOAD:
return L;
case FLOAD:
return F;
case DLOAD:
return D;
case ALOAD:
return A;
default:
return Store;
}
}
boolean isStoreInsn() {
return this == Store;
}
void appendTo(InsnList list, int varIndex, String type) {
VarInsnNode load = new VarInsnNode(opcode, varIndex);
list.add(load);
}
}
private static class LoadInsnInfo {
LoadInsn insn;
int index;
LoadInsnInfo(LoadInsn insn, int index) {
this.insn = insn;
this.index = index;
}
void appendTo(InsnList list, Segment segment, S.Buffer paramNames) {
LocalVariableMetaInfo var = var(segment);
if (null == var) return;
LdcInsnNode ldc = new LdcInsnNode(var.name());
list.add(ldc);
insn.appendTo(list, index, var.type());
MethodInsnNode invokeRenderArg = new MethodInsnNode(INVOKEVIRTUAL, AsmTypes.MAILER_CONTEXT_INTERNAL_NAME, RENDER_NM, RENDER_DESC, false);
list.add(invokeRenderArg);
if (paramNames.length() != 0) {
paramNames.append(',');
}
paramNames.append(var.name());
}
LocalVariableMetaInfo var(Segment segment) {
Label lbl = segment.startLabel;
int pos = -1;
List lblList = segment.trans.lblList;
while (null != lbl) {
LocalVariableMetaInfo var = segment.meta.localVariable(index, lbl);
if (null != var) return var;
if (-1 == pos) {
pos = lblList.indexOf(lbl);
if (pos <= 0) {
return null;
}
}
lbl = lblList.get(--pos);
}
logger.warn("Unable to locate var name for param #%n, possibly because source is compiled without debug info", index);
return null;
}
@Override
public String toString() {
return S.fmt("%sLoad %s", insn, index);
}
}
}
private static final String GET_MAILER_CTX_DESC = "()" + AsmTypes.MAILER_CONTEXT_DESC;
private static final String RENDER_NM = "renderArg";
private static final String RENDER_DESC = AsmTypes.methodDesc(MailerContext.class, String.class, Object.class);
private static final String TEMPLATE_LITERAL = "templateLiteral";
private static final String SENDER_PATH_NM = "senderPath";
private static final String TEMPLATE_LITERAL_DESC = AsmTypes.methodDesc(MailerContext.class, String.class);
private static final String SENDER_LITERAL_DESC = AsmTypes.methodDesc(MailerContext.class, String.class, String.class);
private static final String RENDER_ARG_NAMES_NM = "__appRenderArgNames";
private static final String RENDER_ARG_NAMES_DESC = AsmTypes.methodDesc(MailerContext.class, String.class);
}