//
// Copyright 2007-2010 Qianyan Cai
// Under the terms of the GNU Lesser General Public License version 2.1
//
package objot.aspect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import objot.aspect.Aspect.Targ;
import objot.bytecode.Annotation;
import objot.bytecode.Bytecode;
import objot.bytecode.Code;
import objot.bytecode.CodeCatchs;
import objot.bytecode.CodeLines;
import objot.bytecode.CodeVars;
import objot.bytecode.Constants;
import objot.bytecode.Instruction;
import objot.bytecode.Opcode;
import objot.bytecode.Procedure;
import objot.container.Inject;
import objot.util.Bytes;
import objot.util.Class2;
import objot.util.Mod2;
import static objot.bytecode.Opcode.*;
final class WeaveProc
{
private Bytes targetName;
private Bytecode y;
private Constants cons;
private int clazzCi;
private Code ao;
private Procedure wp;
private Class<?> returnCla;
private Code wo;
private Instruction ws;
private int nameStrCi;
private int descStrCi;
private int targetStrCi;
WeaveProc(Class<?> target, Bytecode y_, Code ao_)
{
targetName = Bytecode.utf(target.getName() + ".");
y = y_;
cons = y.cons;
clazzCi = cons.addClass(target);
ao = ao_;
wp = new Procedure(cons);
wo = new Code(cons, ao.bytes, ao.beginBi);
wp.setCode(wo);
}
void method(Method m, int pi, int datasCi)
{
wp.setModifier(m.getModifiers() & Mod2.PUBLIC_PROTECT);
wp.setNameCi(cons.addUcs(m.getName()));
wp.setDescCi(cons.addUcs(Class2.descript(m)));
returnCla = m.getReturnType();
int[] ads = new int[ao.getAddrN() + 1];
ws = new Instruction(cons, ao.getAddrN() + 200);
for (int ad = 0, adn; ad < ao.getAddrN(); ad += adn)
{
ads[ad] = ws.addr;
adn = ao.getInsAddrN(ad);
if ( !opLocal(ad) && !opReturn(ad) && !opAspect(ad, pi, datasCi))
opCopy(ad, adn);
}
ads[ao.getAddrN()] = ws.addr;
wo.setLocalN(wo.getLocalN() + wp.getParamLocalN() + 2 /* return value */);
post(ads);
}
void ctor(Constructor<?> t)
{
wp.setModifier(t.getModifiers() & Mod2.PUBLIC_PROTECT);
wp.setNameCi(cons.addUtf(Weaver.CTOR_NAME));
wp.setDescCi(cons.addUcs(Class2.descript(t)));
returnCla = void.class;
int[] ads = new int[ao.getAddrN() + 1];
ws = new Instruction(cons, ao.getAddrN() + 200);
for (int ad = 0, adn; ad < ao.getAddrN(); ad += adn)
{
ads[ad] = ws.addr;
adn = ao.getInsAddrN(ad);
if ( !opLocal(ad) && !opCtor(ad))
opCopy(ad, adn);
}
ads[ao.getAddrN()] = ws.addr;
wo.setLocalN(wo.getLocalN() + wp.getParamLocalN());
post(ads);
if (t.isAnnotationPresent(Inject.class))
{
byte[] inject = new byte[4];
Bytes.writeU2(inject, 0, cons.addClass(Inject.class));
wp.getAnnos().addAnno(new Annotation(cons, inject, 0));
}
}
void post(int[] ads)
{
for (int ad = 0; ad < ao.getAddrN(); ad += ao.getInsAddrN(ad))
opJump(ad, ads);
wo.setIns(ws, false);
y.getProcs().addProc(wp);
CodeCatchs cc = wo.getCatchs();
for (int i = 0; cc != null && i < cc.getCatchN(); i++)
cc.setInfo(i, ads[cc.getBeginAd(i)], ads[cc.getEnd1Ad(i)], ads[cc.getCatchAd(i)],
cc.getTypeCi(i));
CodeLines cl = wo.getLines();
for (int i = 0; cl != null && i < cl.getLineN(); i++)
cl.setInfo(i, ads[cl.getBeginAd(i)], cl.getLine(i));
CodeVars cv = wo.getVars();
for (int i = 0, b; cv != null && i < cv.getVarN(); i++)
cv.setInfo(i, b = ads[cv.getBeginAd(i)], ads[cv.getEnd1Ad(i)] - b, //
cv.getNameCi(i), cv.getDescCi(i), //
cv.getLocal(i) == 0 ? 0 : cv.getLocal(i) + wp.getParamLocalN());
cv = wo.getVarSigns();
for (int i = 0, b; cv != null && i < cv.getVarN(); i++)
cv.setInfo(i, b = ads[cv.getBeginAd(i)], ads[cv.getEnd1Ad(i)] - b, //
cv.getNameCi(i), cv.getDescCi(i), //
cv.getLocal(i) == 0 ? 0 : cv.getLocal(i) + wp.getParamLocalN());
}
private boolean opLocal(int ad)
{
int inc = Integer.MAX_VALUE;
byte op = Opcode.getNormalLocalOp(ao, ad);
switch (op)
{
case LLOAD:
case DLOAD:
case ALOAD:
case ILOAD:
case FLOAD:
case LSTORE:
case DSTORE:
case ASTORE:
case ISTORE:
case FSTORE:
case RET:
break;
case IINC:
inc = ao.getInsS1(ad) != WIDE ? ao.getInsS1(ad + 2) : ao.getInsS2(ad + 4);
break;
default:
return false;
}
int i = Opcode.getLocalIndex(ao, ad);
if (i == 0)
ws.ins0(ALOAD0);
else
{
i += wp.getParamLocalN();
if (inc != Integer.MAX_VALUE)
ws.insWideInc(i, inc);
else
ws.insU1wU2(op, i);
}
return true;
}
private boolean opReturn(int ad)
{
byte op = ao.getInsS1(ad);
if (op != RETURN)
return false;
if (wp.getReturnTypeChar() != 'V')
ws.insU1wU2(Opcode.getLoadOp(wp.getReturnTypeChar()), ao.getLocalN()
+ wp.getParamLocalN());
ws.ins0(Opcode.getReturnOp(wp.getReturnTypeChar()));
return true;
}
private boolean opAspect(int ad, int pi, int datasCi)
{
if (ao.getInsS1(ad) != INVOKESTATIC)
return false;
int ci = ao.getInsU2(ad + 1);
if ( !cons.equalsUtf(cons.getCprocClass(ci), Weaver.TARGET_NAME))
return false;
Bytes name = cons.getUtf(cons.getCprocName(ci));
if (Targ.data.utf.equals(name))
targData(pi, datasCi);
else if (Targ.name.utf.equals(name))
targName();
else if (Targ.descript.utf.equals(name))
targDescript();
else if (Targ.target.utf.equals(name))
targTarget();
else if (Targ.thiz.utf.equals(name))
targThiz();
else if (Targ.clazz.utf.equals(name))
targClazz();
else if (Targ.invoke.utf.equals(name))
targInvoke();
else if (Targ.returnClass.utf.equals(name))
targReturnClass();
else if (Targ.getReturn.utf.equals(name))
targGetReturn();
else if (Targ.setReturn.utf.equals(name))
targSetReturn();
else
throw new AssertionError();
return true;
}
private void opCopy(int ad, int adn)
{
byte op = ao.getInsS1(ad);
if (op == LOOKUPSWITCH || op == TABLESWITCH)
{
ws.ins0(op);
ws.insU2((byte)0, 0);
int h = 4 - (ad & 3);
ws.addr = ws.addr - (ws.addr & 3);
ws.copyFrom(ao, ad + h, ao.getInsAddrN(ad) - h);
}
else
ws.copyFrom(ao, ad, adn);
}
private void targData(int pi, int datasCi)
{
ws.insU2(GETSTATIC, datasCi);
ws.insS2(SIPUSH, pi);
ws.ins0(AALOAD);
wo.setStackN(wo.getStackN() + 1);
}
private void targName()
{
if (nameStrCi <= 0)
nameStrCi = cons.addString(wp.getNameCi());
ws.insU2(LDCW, nameStrCi);
}
private void targDescript()
{
if (descStrCi <= 0)
descStrCi = cons.addString(wp.getDescCi());
ws.insU2(LDCW, descStrCi);
}
private void targTarget()
{
if (targetStrCi <= 0)
{
Bytes b = new Bytes(targetName);
int n = wp.getNameCi();
int d = wp.getDescCi();
b.addByteN(cons.getUtfByteN(n) + cons.getUtfByteN(d));
cons.getUtfTo(n, b, targetName.byteN());
cons.getUtfTo(d, b, targetName.byteN() + cons.getUtfByteN(n));
targetStrCi = cons.addString(cons.addUtf(b));
}
ws.insU2(LDCW, targetStrCi);
}
private void targThiz()
{
ws.ins0(ALOAD0);
}
private void targClazz()
{
ws.insU2(LDCW, clazzCi);
}
private void targInvoke()
{
ws.ins0(ALOAD0);
Bytes desc = cons.getUtf(wp.getDescCi());
for (int i = 1, b = desc.beginBi + 1; desc.bytes[b] != ')';)
{
ws.insU1wU2(Opcode.getLoadOp((char)desc.bytes[b]), i);
i += Opcode.getLocalStackN((char)desc.bytes[b]);
b += Bytecode.typeDescByteN(desc, b - desc.beginBi);
}
ws.insU2(INVOKESPECIAL, cons.addCproc(y.head.getSuperCi(), cons.addNameDesc(
wp.getNameCi(), wp.getDescCi())));
if (wp.getReturnTypeChar() != 'V')
ws.insU1wU2(Opcode.getStoreOp(wp.getReturnTypeChar()), ao.getLocalN()
+ wp.getParamLocalN());
wo.setStackN(wo.getStackN() + 2 /* e.g. return long */+ wp.getParamLocalN());
}
private void targReturnClass()
{
ws.insLoad(returnCla);
}
private void targGetReturn()
{
if (returnCla == void.class)
ws.ins0(ACONSTNULL);
else
ws.insU1wU2(Opcode.getLoadOp(wp.getReturnTypeChar()), ao.getLocalN()
+ wp.getParamLocalN());
ws.insBox(returnCla);
}
private void targSetReturn()
{
if (returnCla == void.class)
ws.ins0(POP);
else
{
ws.insUnboxNarrow(returnCla);
ws.insU1wU2(Opcode.getStoreOp(wp.getReturnTypeChar()), ao.getLocalN()
+ wp.getParamLocalN());
}
}
private boolean opCtor(int ad)
{
if (ao.getInsS1(ad) != INVOKESPECIAL)
return false;
int ci = ao.getInsU2(ad + 1);
if ( !cons.equalsUtf(cons.getCprocClass(ci), Weaver.ASPECT_NAME))
return false;
Bytes desc = cons.getUtf(wp.getDescCi());
for (int i = 1, b = desc.beginBi + 1; desc.bytes[b] != ')';)
{
ws.insU1wU2(Opcode.getLoadOp((char)desc.bytes[b]), i);
i += Opcode.getLocalStackN((char)desc.bytes[b]);
b += Bytecode.typeDescByteN(desc, b - desc.beginBi);
}
ws.insU2(INVOKESPECIAL, cons.addCproc(y.head.getSuperCi(), cons.addNameDesc(
wp.getNameCi(), wp.getDescCi())));
wo.setStackN(wo.getStackN() + wp.getParamLocalN());
return true;
}
private void opJump(int ad, int[] ads)
{
switch (ao.getInsS1(ad))
{
case GOTO:
case IFAE:
case IFAN:
case IFIE:
case IFIE0:
case IFIG:
case IFIG0:
case IFIGE:
case IFIGE0:
case IFIL:
case IFIL0:
case IFILE:
case IFILE0:
case IFIN:
case IFIN0:
case IFNOTNULL:
case IFNULL:
case JSR:
ws.writeS2(ads[ad] + 1, ads[ad + ao.getInsS2(ad + 1)] - ads[ad]);
break;
case GOTO4:
case JSR4:
ws.writeS4(ads[ad] + 1, ads[ad + ao.getInsS4(ad + 1)] - ads[ad]);
break;
case LOOKUPSWITCH:
{
int a = ad + 4 - (ad & 3);
int wa = ads[ad] + 4 - (ads[ad] & 3);
ws.writeS4(wa, ads[ad + ao.getInsS4(a)] - ads[ad]);
int n = ao.getInsU4(a + 4);
a += 12;
wa += 12;
for (; n > 0; n--, a += 8, wa += 8)
ws.writeS4(wa, ads[ad + ao.getInsS4(a)] - ads[ad]);
break;
}
case TABLESWITCH:
{
int a = ad + 4 - (ad & 3);
int wa = ads[ad] + 4 - (ads[ad] & 3);
ws.writeS4(wa, ads[ad + ao.getInsS4(a)] - ads[ad]);
int n = ao.getInsS4(a + 8) - ao.getInsS4(a + 4) + 1;
a += 12;
wa += 12;
for (; n > 0; n--, a += 4, wa += 4)
ws.writeS4(wa, ads[ad + ao.getInsS4(a)] - ads[ad]);
break;
}
}
}
}