package me.august.lumen.compile.parser.ast.expr; import me.august.lumen.common.BytecodeUtil; import me.august.lumen.compile.analyze.var.LocalVariable; import me.august.lumen.compile.analyze.var.VariableReference; import me.august.lumen.compile.codegen.BuildContext; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; // We're pretending to use the POP instruction, because // the same rules apply to incrementing. public class IncrementExpr extends UnaryExpression { public enum Op { INC(1), DEC(-1); Op(int delta) { this.delta = delta; } private int delta; } private VariableExpression value; private Op op; private boolean postfix; // whether or not the variable getter should be generated: // e.g. "foo++" should not generate the variable getter, // but "bar = foo++" should generate the variable getter. private boolean shouldGen = true; public IncrementExpr(Expression value, Op op, boolean postfix) { super(value); if (!(value instanceof VariableExpression)) throw new IllegalArgumentException("Invalid type, expected variable"); this.value = (VariableExpression) value; this.op = op; this.postfix = postfix; } @Override public Type expressionType() { return value.expressionType(); } @Override public void generate(MethodVisitor visitor, BuildContext context) { if (!BytecodeUtil.isNumeric(expressionType()) || expressionType().getSort() > Type.INT) { context.error( "Incompatible type: " + expressionType(), false, this ); } // If it's postfix we load the value // *before* incrementing, so the un-incremented // value is pushed onto the stack. if (postfix && shouldGen) { value.generate(visitor, context); } // if it's a local variable, use IINC instruction VariableReference var = value.getVariableReference(); if (var instanceof LocalVariable) { LocalVariable local = (LocalVariable) var; visitor.visitIincInsn(local.getIndex(), op.delta); } else { // otherwise: load, add 1, save var.generateSetCode(visitor, (m) -> { var.generateGetCode(m); BytecodeUtil.pushInt(m, op.delta); m.visitInsn(Opcodes.IADD); }); } // If it's not postfix we load the value // *after* incrementing, so the incremented // value is pushed onto the stack. if (!postfix && shouldGen) { value.generate(visitor, context); } } @Override public void markAsTopLevelStatement(boolean flag) { shouldGen = !flag; } }