package me.august.lumen.compile.parser.ast.expr;
import me.august.lumen.common.BytecodeUtil;
import me.august.lumen.compile.codegen.Branch;
import me.august.lumen.compile.codegen.BuildContext;
import me.august.lumen.compile.codegen.Conditional;
import me.august.lumen.compile.codegen.MethodCodeGen;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class EqExpr extends BinaryExpression implements Conditional {
public enum Op {
EQ, NE
}
private Op op;
public EqExpr(Expression left, Expression right, Op op) {
super(left, right);
this.op = op;
}
@Override
public Branch branch(MethodCodeGen ifBranch, MethodCodeGen elseBranch) {
Label label = new Label();
MethodCodeGen cond = null;
Type lType = left.expressionType();
Type rType = right.expressionType();
// if the left side is null and the right side is an
// object, use IFNONNULL or IFNULL with the right side
// value on the operand stack
if (left instanceof NullExpr && BytecodeUtil.isPrimitive(rType)) {
int opcode = op == Op.EQ ? Opcodes.IFNONNULL : Opcodes.IFNULL;
cond = (m, c) -> {
right.generate(m, c);
m.visitJumpInsn(opcode, label);
};
// if the right side is null and the left side is an
// object, use IFNONNULL or IFNULL with the left side
// value on the operand stack
} else if (right instanceof NullExpr && BytecodeUtil.isObject(lType)) {
int opcode = op == Op.EQ ? Opcodes.IFNONNULL : Opcodes.IFNULL;
cond = (m, c) -> {
left.generate(m, c);
m.visitJumpInsn(opcode, label);
};
// if the left and right side are both objects, use
// IF_ACMPEQ or IF_ACMPNE with both values on the
// operand stack
} else if (BytecodeUtil.isObject(lType) && BytecodeUtil.isObject(rType)) {
int opcode = op == Op.EQ ? Opcodes.IF_ACMPNE : Opcodes.IF_ACMPEQ;
cond = (m, c) -> {
left.generate(m, c);
right.generate(m, c);
m.visitJumpInsn(opcode, label);
};
// if the left and right side are both numeric primitives:
// 1) get the widest type of the two
// 2) promote all types narrower than int to int
// 3) cast left and right to widest type if necessary
// 4) if the widest type is a double, float, or long get
// the comparison opcode needed
// 5) jump using IFNE/IFEQ or IF_ICMPNE/IF_ICMPEQ
} else if (BytecodeUtil.isNumeric(lType) && BytecodeUtil.isNumeric(rType)) {
Type widest = BytecodeUtil.widest(lType, rType);
if (widest.getSort() < Type.INT) widest = Type.INT_TYPE;
if (lType.getSort() < Type.INT) lType = Type.INT_TYPE;
if (rType.getSort() < Type.INT) rType = Type.INT_TYPE;
int lcast = BytecodeUtil.castNumberOpcode(lType, widest);
int rcast = BytecodeUtil.castNumberOpcode(rType, widest);
int cmp = BytecodeUtil.compareOpcode(widest);
final int opcode;
if (cmp >= 0) {
opcode = op == Op.EQ ? Opcodes.IFNE : Opcodes.IFEQ;
} else {
opcode = op == Op.EQ ? Opcodes.IF_ICMPNE : Opcodes.IF_ICMPEQ;
}
cond = (m, c) -> {
left.generate(m, c);
if (lcast >= 0) m.visitInsn(lcast);
right.generate(m, c);
if (rcast >= 0) m.visitInsn(rcast);
if (cmp >= 0) m.visitInsn(cmp);
m.visitJumpInsn(opcode, label);
};
}
return new Branch(cond, label, ifBranch, elseBranch);
}
@Override
public void generate(MethodVisitor visitor, BuildContext context) {
branch(Conditional.PUSH_TRUE, Conditional.PUSH_FALSE).generate(visitor, context);
}
}